diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9eb0f1d5b1..7f1a4fd25d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: fail-fast: true matrix: os: [ubuntu-24.04, ubuntu-24.04-arm] - compiler: [ [clang++-20, clang-20, "clang-20 libclang-rt-20-dev clang-tools-20"] ] + compiler: [ [clang++-21, clang-21, "clang-21 libclang-rt-21-dev clang-tools-21"] ] build: [ Debug, Release, DebugLibdeps, DebugCov ] llvm-version: [ 16, "22.1" ] include: @@ -31,7 +31,7 @@ jobs: - build: DebugCov cmake_build_type: Debug flags: -DCODE_COVERAGE=ON - extra_dependencies: llvm-20 # For coverage + extra_dependencies: llvm-21 # For coverage - llvm-version: 16 llvm-major-version: 16 - llvm-version: "22.1" @@ -75,6 +75,7 @@ jobs: -DBUILD_PHASAR_CLANG=OFF \ -DPHASAR_USE_Z3=ON \ -DPHASAR_BUILD_MODULES=ON \ + -DPHASAR_TARGET_ARCH="" \ -DPHASAR_LLVM_VERSION=${{ matrix.llvm-version }} \ ${{ matrix.flags }} \ -G Ninja diff --git a/CMakeLists.txt b/CMakeLists.txt index e0f081ae3f..15809d5157 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -270,6 +270,9 @@ include(add_nlohmann_json) add_nlohmann_json() add_json_schema_validator() +# TBB +find_package(TBB) + # Coverage if (CODE_COVERAGE) set(CODE_COVERAGE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/code-coverage.cmake") @@ -415,6 +418,10 @@ target_sources(phasar_interface INTERFACE BASE_DIRS "${PHASAR_SRC_DIR}/include" "${PHASAR_BINARY_DIR}/include" FILES ${PHASAR_PUBLIC_HEADERS} "${PHASAR_BINARY_DIR}/include/phasar/Config/phasar-config.h" ) +if (TARGET TBB::tbb) + target_link_libraries(phasar_interface INTERFACE TBB::tbb) +endif() + # Some preprocessor symbols that need to be available in phasar sources, but should not be installed add_cxx_compile_definitions(PHASAR_SRC_DIR="${CMAKE_SOURCE_DIR}") diff --git a/config/double-free-config.json b/config/double-free-config.json index 043b392288..e5595cc093 100644 --- a/config/double-free-config.json +++ b/config/double-free-config.json @@ -23,6 +23,17 @@ 0 ] } + }, + { + "name": "_ZdaPv", + "params": { + "source": [ + 0 + ], + "sink": [ + 0 + ] + } } ] } diff --git a/examples/how-to/07-write-ifds-analysis/simple.cpp b/examples/how-to/07-write-ifds-analysis/simple.cpp index 34ea367b47..b6b07a1b4a 100644 --- a/examples/how-to/07-write-ifds-analysis/simple.cpp +++ b/examples/how-to/07-write-ifds-analysis/simple.cpp @@ -34,13 +34,14 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem { /// Provides the initial seeds, i.e., the pairs that are assumed /// to hold un-conditionally at the beginning of the analysis. /// This is the start state that the IFDS solver will use to start with. - [[nodiscard]] psr::InitialSeeds initialSeeds() override { + [[nodiscard]] psr::InitialSeeds initialSeeds() { psr::InitialSeeds Seeds; psr::LLVMBasedCFG CFG; // Here, we just say that for all entry-functions in the EntryPoints, the // zero-value should hold at the very first statement. - addSeedsForStartingPoints(EntryPoints, IRDB, CFG, Seeds, getZeroValue()); + addSeedsForStartingPoints(EntryPoints, getProjectIRDB(), CFG, Seeds, + getZeroValue()); return Seeds; }; @@ -48,8 +49,8 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem { /// Here, we define special semantics of function-calls that are specified /// outside of the target program. In the case of taint analysis, we need to /// handle sources, sinks and sanitizers here: - [[nodiscard]] FlowFunctionPtrType - getSummaryFlowFunction(n_t CallSite, f_t DestFun) override { + [[nodiscard]] FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, + f_t DestFun) { const auto *CS = llvm::cast(CallSite); // Process the effects of source or sink functions that are called @@ -60,7 +61,7 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem { if (Gen.empty() && Leak.empty() && Kill.empty()) { // This CallSite apparently is not calling a special source/sink/sanitizer // function. Fallback to the default-behavior. - return DefaultAliasAwareIFDSProblem::getSummaryFlowFunction(CS, DestFun); + return nullptr; } // Since our analysis is alias-aware, we must handle aliasing here: @@ -76,13 +77,13 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem { return Gen; } - if (Leak.count(Source)) { + if (Leak.contains(Source)) { // In case of a sink, we create a leak if one of the leaking parameters // (Leak) is tainted (Source). Leaks.insert(CS); } - if (Kill.count(Source)) { + if (Kill.contains(Source)) { // In case of a sanitizer, we kill tainted values (Source) that flow // into the sanitizied parameters (Kill). return {}; diff --git a/examples/how-to/08-write-ide-analysis/simple.cpp b/examples/how-to/08-write-ide-analysis/simple.cpp index 45443ced08..d8d5ce7444 100644 --- a/examples/how-to/08-write-ide-analysis/simple.cpp +++ b/examples/how-to/08-write-ide-analysis/simple.cpp @@ -54,19 +54,19 @@ class ExampleLinearConstantAnalysis /// to hold un-conditionally at the beginning of the analysis. /// Similar to IFDS, this is the start state that the IDE solver will use to /// start with. - [[nodiscard]] psr::InitialSeeds initialSeeds() override { + [[nodiscard]] psr::InitialSeeds initialSeeds() { psr::InitialSeeds Seeds; psr::LLVMBasedCFG CFG; // Here, we just say that for all entry-functions in the EntryPoints, the // zero-value should hold at the very first statement. - addSeedsForStartingPoints(EntryPoints, IRDB, CFG, Seeds, getZeroValue(), - bottomElement()); + addSeedsForStartingPoints(EntryPoints, getProjectIRDB(), CFG, Seeds, + getZeroValue(), bottomElement()); return Seeds; }; - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { if (const auto *Alloca = llvm::dyn_cast(Curr)) { // Freshly allocated variables hold no constant value @@ -88,7 +88,7 @@ class ExampleLinearConstantAnalysis return this->DefaultNoAliasIDEProblem::getNormalFlowFunction(Curr, Succ); } - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override { + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) { // We definitely want to re-use as much as possible from the default // call-flow-function auto DefaultFn = @@ -118,7 +118,7 @@ class ExampleLinearConstantAnalysis } FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitInst, n_t RetSite) override { + n_t ExitInst, n_t RetSite) { auto DefaultFn = this->DefaultNoAliasIDEProblem::getRetFlowFunction( CallSite, CalleeFun, ExitInst, RetSite); @@ -352,8 +352,7 @@ class ExampleLinearConstantAnalysis }; psr::EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, - n_t /*Succ*/, - d_t SuccNode) override { + n_t /*Succ*/, d_t SuccNode) { if (isZeroValue(CurrNode) && !isZeroValue(SuccNode)) { // Handle the two cases, where we generate facts from zero: @@ -384,8 +383,11 @@ class ExampleLinearConstantAnalysis } // Attach the arithmetic transformer to this edge - return BinOp{Op, llvm::dyn_cast(Lop), - llvm::dyn_cast(Rop)}; + return BinOp{ + .OpCode = Op, + .LeftConst = llvm::dyn_cast(Lop), + .RightConst = llvm::dyn_cast(Rop), + }; } // Pass everything else as identity @@ -394,7 +396,7 @@ class ExampleLinearConstantAnalysis psr::EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, f_t /*DestinationFunction*/, - d_t DestNode) override { + d_t DestNode) { if (isZeroValue(SrcNode) && !isZeroValue(DestNode)) { // If a constant int is passed as parameter, we need to generate the // parameter inside the callee from zero @@ -408,9 +410,10 @@ class ExampleLinearConstantAnalysis return psr::EdgeIdentity{}; } - psr::EdgeFunction - getReturnEdgeFunction(n_t CallSite, f_t /*CalleeFunction*/, n_t ExitStmt, - d_t ExitNode, n_t /*RetSite*/, d_t RetNode) override { + psr::EdgeFunction getReturnEdgeFunction(n_t CallSite, + f_t /*CalleeFunction*/, + n_t ExitStmt, d_t ExitNode, + n_t /*RetSite*/, d_t RetNode) { if (isZeroValue(ExitNode) && RetNode == CallSite) { // If we return a literal constant int, we must generate the corresponding // value at the call-site from zero, i.e., the CallSite itself in case of @@ -428,7 +431,7 @@ class ExampleLinearConstantAnalysis psr::EdgeFunction getCallToRetEdgeFunction(n_t /*CallSite*/, d_t /*CallNode*/, n_t /*RetSite*/, d_t /*RetSiteNode*/, - llvm::ArrayRef /*Callees*/) override { + llvm::ArrayRef /*Callees*/) { // The call-to-return edge-function handles facts that are not affected by // the call. This is usually the identity function. return psr::EdgeIdentity{}; diff --git a/include/phasar/DB/ProjectIRDB.h b/include/phasar/DB/ProjectIRDB.h index 26e3f93d3d..80afd24c71 100644 --- a/include/phasar/DB/ProjectIRDB.h +++ b/include/phasar/DB/ProjectIRDB.h @@ -14,6 +14,7 @@ #include "llvm/ADT/StringRef.h" #include +#include namespace psr { @@ -112,6 +113,14 @@ concept ProjectIRDB = requires ProjectSymbolTable; }; +template +concept ProjectIRDBPtr = + ProjectIRDB())>>; +template +concept ProjectIRDBConstPtr = + std::is_const_v())>> && + ProjectIRDB())>>; + // NOLINTNEXTLINE(readability-identifier-naming) auto IRDBGetFunctionDef(const ProjectIRDB auto *IRDB) noexcept { return [IRDB](llvm::StringRef Name) { diff --git a/include/phasar/DataFlow.h b/include/phasar/DataFlow.h index d6d52320b0..686f075db4 100644 --- a/include/phasar/DataFlow.h +++ b/include/phasar/DataFlow.h @@ -17,8 +17,12 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/EntryPointUtils.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IDEProblemWrapper.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 1f924320ab..1f46902b85 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -43,6 +43,11 @@ concept IsEdgeFunction = requires(const T &EF, typename T::l_t Src) { { EF.computeTarget(Src) } -> std::convertible_to; }; +template +concept IsEdgeFunctionFor = IsEdgeFunction && requires { + requires std::same_as; +}; + template concept HasEFCompose = requires(EdgeFunctionRef EFRef, const EdgeFunction &EF) { @@ -110,7 +115,7 @@ class EdgeFunctionBase { /// \brief Non-null reference to an edge function that is guarenteed to be /// managed by an EdgeFunction object. template -class [[gsl::Pointer(EF)]] EdgeFunctionRef final : EdgeFunctionBase { +class PSR_POINTER(EF) EdgeFunctionRef final : EdgeFunctionBase { template friend class EdgeFunction; public: @@ -160,7 +165,7 @@ class [[gsl::Pointer(EF)]] EdgeFunctionRef final : EdgeFunctionBase { template // -- combined copy and move assignment // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) -class [[clang::trivial_abi, gsl::Owner]] EdgeFunction final : EdgeFunctionBase { +class PSR_TRIVIAL_ABI PSR_OWNER() EdgeFunction final : EdgeFunctionBase { public: using l_t = L; diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h index e6b4cd2e17..546a0f8dd0 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h @@ -25,6 +25,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/Support/raw_ostream.h" +#include #include #include @@ -269,6 +270,244 @@ template class EdgeFunctions { } }; +template +concept EdgeFunctionFactory = requires( + T &EFF, typename T::n_t Curr, typename T::n_t Succ, + typename T::d_t CurrNode, typename T::d_t SuccNode, + typename T::n_t ExitInst, typename T::n_t CallSite, typename T::n_t RetSite, + typename T::f_t CalleeFun, llvm::ArrayRef Callees) { + typename T::EdgeFunctionType; + typename T::n_t; + typename T::d_t; + typename T::f_t; + typename T::l_t; + + /// + /// Also refer to FlowFunctions::getNormalFlowFunction() + /// + /// Describes a value computation problem along a normal (non-call, + /// non-return) intra-procedural exploded supergraph edge. A normal edge + /// function implementation is queried for each edge that has been generated + /// by appling the flow function returned by + /// FlowFunctions::getNormalFlowFunction(). The supergraph edge whose + /// computation is requested is defined by the supergraph nodes CurrNode and + /// SuccNode. + /// + /// Let instruction_1 := Curr, instruction_2 := Succ, and 0 the tautological + /// lambda fact. + /// + /// The concrete implementation of an edge function e is depending on the + /// analysis problem. In the following, we present a brief, contrived example: + /// + /// Consider the following flow function implementation (cf. + /// FlowFunctions::getNormalFlowfunction()): + /// \code + /// f(0) -> {0} // pass the lambda (or zero fact) as identity + /// f(o) -> {o, x} // generate a new fact x from o + /// f(.) -> {.} // pass all other facts that hold before + /// // instruction_1 as identity + /// \endcode + /// + /// The above flow-function implementation corresponds to the following edges + /// in the exploded supergraph. + /// \code + /// 0 o ... + /// | |\ ... + /// Curr := x = instruction_1 o p | | \ ... + /// | | | ... + /// v v v ... + /// 0 o x ... + /// + /// Succ := y = instruction_2 q r + /// \endcode + /// For each edge generated by the respective flow function a normal edge + /// function is queried that describes a value computation. This results in + /// the following queries: + /// + /// \code + /// getNormalEdgeFunction(0, Curr, 0 Succ); + /// getNormalEdgeFunction(o, Curr, o Succ); + /// getNormalEdgeFunction(o, Curr, x Succ); + /// \endcode + { + EFF.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode) + } -> std::convertible_to; + + /// + /// Also refer to FlowFunctions::getCallFlowFunction() + /// + /// Describes a value computation problem along a call flow. A call edge + /// function is queried for each edge that has been generated by applying the + /// flow function that has been returned by + /// FlowFunctions::getCallFlowFunction. The supergraph edge whose computation + /// is requested is defined by the supergraph nodes SrcNode and DestNode. + /// + /// The concrete implementation of an edge function e is depending on the + /// analysis problem. In the following, we present a brief, contrived example: + /// + /// Consider the following flow function implementation (cf. + /// FlowFunctions::getCallFlowFunction()): + /// \code + /// f(0) -> {0} // pass as identity into the callee target + /// f(o) -> {q} // map actual o into formal q + /// f(p) -> {r} // map actual p into formal r + /// f(.) -> {} // kill all other facts that are not visible to the + /// // callee target + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o p ... + /// \ \ \ ... + /// CallInst := x = CalleeFun(o, p, ...) \ \ +----------------+ + /// \ +---------------- | + /// +-------------+ + | + /// ... | | | + /// ... | | | + /// 0 o p ... | | | + /// | | | + /// | | | + /// | | | + /// Ty CalleeFun(q, r, ...) | | | + /// v v v + /// 0 q r ... + /// + /// start point + /// \endcode + /// For each edge generated by the respective flow function a call edge + /// function is queried that describes a value computation. This results in + /// the following queries: + /// \code + /// getCallEdgeFunction(CallInst, 0, CalleeFun, 0); + /// getCallEdgeFunction(CallInst, o, CalleeFun, q); + /// getCallEdgeFunction(CallInst, p, CalleeFun, r); + /// \endcode + { + EFF.getCallEdgeFunction(CallSite, CurrNode, CalleeFun, SuccNode) + } -> std::convertible_to; + + /// + /// Also refer to FlowFunction::getRetFlowFunction() + /// + /// Describes a value computation problem along a return flow. A return edge + /// function implementation is queried for each edge that has been generated + /// by applying the flow function that has been returned by + /// FlowFunctions::getRetFlowFunction(). The supergraph edge whose computation + /// is requested is defined by the supergraph nodes ExitNode and RetNode. + /// + /// The concrete implementation of an edge function e is depending on the + /// analysis problem. In the following, we present a brief, contrived example: + /// + /// Consider the following flow function implementation (cf. + /// FlowFunctions::getRetFlowFunction()): + /// \code + /// f(0) -> {0} // pass as identity into the callee target + /// f(r) -> {x} // map return value to lhs variable at CallSite + /// f(q) -> {o} // map pointer-typed formal q to actual o + /// f(.) -> {} // kill all other facts that are not visible to the + /// // caller + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o ... + /// + /// CallSite = RetSite := x = CalleeFun(o, ...) + /// +------------------+ + /// +--|---------------+ | + /// +--|--|------------+ | | + /// v v v ... | | | + /// 0 o x ... | | | + /// | | | + /// | | | + /// | | | + /// Ty CalleeFun(q, ...) | | | + /// | | | + /// 0 q r + /// + /// ExitInst := return r + /// \endcode + /// For each edge generated by the respective flow function a return edge + /// function is queried that describes a value computation. This results in + /// the following queries: + /// \code + /// getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, 0, RetSite, 0); + /// getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, q, RetSite, o); + /// getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, r, RetSite, x); + /// \endcode + { + EFF.getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, CurrNode, RetSite, + SuccNode) + } -> std::convertible_to; + + /// + /// Also refer to FlowFunctions::getCallToRetFlowFunction() + /// + /// Describes a value computation problem along data-flows alongsite a + /// CallSite. A return edge function implementation is queried for each edge + /// that has been generated by applying the flow function that has been + /// returned by FlowFunctions::getCallToRetFlowFunction(). The supergraph edge + /// whose computation is requested is defined by the supergraph nodes CallNode + /// and RetSiteNode. + /// + /// The concrete implementation of an edge function e is depending on the + /// analysis problem. In the following, we present a brief, contrived example: + /// + /// Consider the following flow function implementation (cf. + /// FlowFunctions::getCallToRetFlowFunction()): + /// \code + /// f(0) -> {0} // pass lambda as identity alongsite the CallSite + /// f(o) -> {o} // assuming that o is passed by value, it is passed + /// // alongsite the CallSite + /// f(p) -> {} // assuming that p is a pointer-typed value, we need + /// // to kill p, as it will be handled by the call- and + /// // return-flow functions + /// f(.) -> {.} // pass everything that is not involved in the call + /// // as identity + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o ... + /// | | + /// | +-------+ + /// +--------+ | + /// | | + /// CallSite = RetSite := x = CalleeFun(o, p, ...) | | + /// | | + /// +--------+ | + /// | +-------+ + /// v v + /// 0 o x ... + /// \endcode + /// For each edge generated by the respective flow function a call-to-return + /// edge function is queried that describes a value computation. This results + /// in the following queries: + /// + /// getCallToRetEdgeFunction(CallSite, 0, RetSite, 0, {CalleeFun}); + /// getCallToRetEdgeFunction(CallSite, o, RetSite, o, {CalleeFun}); + /// + { + EFF.getCallToRetEdgeFunction(CallSite, CurrNode, RetSite, SuccNode, Callees) + } -> std::convertible_to; + + /// + /// Also refer to FlowFunction::getSummaryFlowFunction() + /// + /// Describes a value computation problem along a summary data flow. A summary + /// edge function implementation is queried for each edge that has been + /// generated by FlowFunctions::getSummaryFlowFunction(). The supergraph edge + /// whose computation is requested is defined by the supergraph nodes CurrNode + /// and SuccNode. + /// + /// The default implementation returns a nullptr to indicate that the + /// mechanism should not be used. + /// + { + EFF.getSummaryEdgeFunction(CallSite, CurrNode, RetSite, SuccNode) + } -> std::convertible_to; +}; + } // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index ec18592c24..b932709475 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -23,6 +23,7 @@ #include "llvm/ADT/ArrayRef.h" +#include #include #include #include @@ -50,6 +51,7 @@ template > class FlowFunction { using container_type = Container; using value_type = typename container_type::value_type; + using d_t = D; virtual ~FlowFunction() = default; @@ -67,10 +69,49 @@ template > class FlowFunction { virtual container_type computeTargets(D Source) = 0; }; +template +concept IsFlowFunctionLike = requires(T &FF, typename T::d_t Source) { + typename T::d_t; + typename T::container_type; + requires is_iterable_over_v; + { + FF.computeTargets(Source) + } -> std::convertible_to; +}; + +template +concept IsFlowFunctionPtr = + IsFlowFunctionLike())>>; + +template +concept IsFlowFunctionOf = + IsFlowFunctionLike && std::same_as; + +template +concept IsFlowFunctionPtrOf = + IsFlowFunctionOf())>, D>; + +template +concept IsFlowFunctionOrFFPtr = IsFlowFunctionLike || IsFlowFunctionPtr; + +template +concept IsFlowFunctionOrFFPtrOf = + IsFlowFunctionOf || IsFlowFunctionPtrOf; + +template +using FFContainerType = typename decltype([] { + if constexpr (IsFlowFunctionLike) { + return std::type_identity{}; + } else { + return std::type_identity())>::container_type>{}; + } +}())::type; + /// Helper template to check at compile-time whether a type implements the /// FlowFunction interface, no matter which data-flow fact type it uses. /// -/// Use is_flowfunction_v instead. +/// Use IsFlowFunctionLike instead. template struct IsFlowFunction { template static std::true_type test(const FlowFunction &); @@ -84,10 +125,12 @@ template struct IsFlowFunction { /// Helper template to check at compile-time whether a type implements the /// FlowFunction interface, no matter which data-flow fact type it uses. template -concept is_flowfunction_v = IsFlowFunction::value; // NOLINT +concept is_flowfunction_v + [[deprecated("Use IsFlowFunctionLike instead. It is more flexible")]] = + IsFlowFunction::value; // NOLINT /// Given a flow-function type FF, returns a (smart) pointer type pointing to FF -template +template using FlowFunctionPtrTypeOf = MaybeUniquePtr; /// Given a dataflow-fact type and optionally a container-type, returns a @@ -886,6 +929,275 @@ class FlowFunctions } }; +template +concept FlowFunctionFactory = + requires(T &FFF, typename T::n_t Curr, typename T::n_t Succ, + typename T::n_t ExitInst, typename T::n_t CallSite, + typename T::n_t RetSite, typename T::f_t CalleeFun, + llvm::ArrayRef Callees) { + typename T::FlowFunctionPtrType; + typename T::d_t; + typename T::f_t; + typename T::n_t; + typename T::container_type; + + requires is_iterable_over_v; + requires IsFlowFunctionPtrOf; + requires std::same_as>; + + /// + /// Describes the effects of the current instruction, i.e. data-flows, + /// along normal (non-call, non-return) instructions. Analysis writers are + /// free to inspect the successor instructions, too, as a lookahead. + /// + /// Let instruction_1 := Curr, instruction_2 := Succ, and 0 the + /// tautological lambda fact. + /// + /// The returned flow function implementation f + /// (FlowFunction::computeTargets()) is applied to each data-flow fact d_i + /// that holds before the current statement under analysis. f's return + /// type is a set of (target) facts that have to be generated from the + /// source fact d_i by the data-flow solver. Each combination of input + /// fact d_i (given as an input to f) and respective output facts (f(d_i)) + /// represents an edge that must be "drawn" to construct the exploded + /// supergraph for the analysis problem to be solved. + /// + /// The concrete implementation of f is depending on the analysis problem. + /// In the following, we present a brief, contrived example: + /// + /// f is applied to each data-flow fact d_i that holds before + /// instruction_1. We assume that f is implemented to produce the + /// following outputs. + /// \code + /// f(0) -> {0} // pass the lambda (or zero fact) as identity + /// f(o) -> {o, x} // generate a new fact x from o + /// f(.) -> {.} // pass all other facts that hold before + /// // instruction_1 as identity + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o ... + /// | |\ ... + /// x = instruction_1 o p | | \ ... + /// | | | ... + /// v v v ... + /// 0 o x ... + /// + /// y = instruction_2 q r + /// \endcode + { + FFF.getNormalFlowFunction(Curr, Succ) + } -> std::convertible_to; + + /// + /// Handles call flows: describes the effects of a function call at + /// callInst to the callee target destFun. If a call instruction has + /// multiple callee targets, for instance, because it is an indirect + /// function call that cannot be analyzed precisely in a static manner, + /// the call flow function will be queried for each callee target. + /// + /// This flow function usually handles parameter passing and maps actual + /// to formal parameters. If an analysis writer does not wish to analyze a + /// given callee target they can return a flow function implementation + /// that kills all data-flow facts (e.g. KillAll) such that call is not + /// followed. A commonly used trick to model the effects of functions that + /// are not present (e.g. library functions such as malloc(), free(), + /// etc.) is to kill all facts at the call to the respective target and + /// plugin the semantics in the call-to-return flow function. In the + /// call-to-return flow function, an analysis writer can check if the + /// function of interest is one of the possible targets and then, return a + /// flow function implementation that describes the special semantics of + /// that function call. + /// + /// Let start_point be the starting point of the callee target CalleeFun. + /// + /// The returned flow function implementation f + /// (FlowFunction::computeTargets()) is applied to each data-flow fact d_i + /// that holds right before the CallInst. f's return type is a set of + /// (target) facts that have to be generated from the source fact d_i by + /// the data-flow solver. Each target fact that is generated will hold + /// before start_point. + /// + /// The concrete implementation of f is depending on the analysis problem. + /// In the following, we present a brief, contrived example: + /// + /// f is applied to each data-flow fact d_i that holds before CallInst. We + /// assume that f is implemented to produce the following outputs. + /// \code + /// f(0) -> {0} // pass as identity into the callee target + /// f(o) -> {q} // map actual o into formal q + /// f(p) -> {r} // map actual p into formal r + /// f(.) -> {} // kill all other facts that are not visible to + /// the + /// // callee target + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o p ... + /// \ \ \ ... + /// x = CalleeFun(o, p, ...) \ \ +----------------+ + /// \ +---------------- | + /// +-------------+ + | + /// ... | | | + /// ... | | | + /// 0 o p ... | | | + /// | | | + /// | | | + /// | | | + /// Ty CalleeFun(q, r, ...) | | | + /// v v v + /// 0 q r ... + /// + /// start point + /// \endcode + { + FFF.getCallFlowFunction(Curr, CalleeFun) + } -> std::convertible_to; + + /// + /// Handles return flows: describes the data-flows from an ExitInst to the + /// corresponding RetSite. + /// + /// This flow function usually handles the returned value of the callee + /// target as well as the parameter mapping back to the caller of + /// CalleeFun for pointer parameters as modifications made by CalleeFun + /// are visible to the caller. Data-flow facts that are not returned or + /// escape via function pointer parameters (or global variables) are + /// usually killed. + /// + /// The returned flow function implementation f + /// (FlowFunction::computeTargets()) is applied to each data-flow fact d_i + /// that holds right before the ExitInst. f's return type is a set of + /// (target) facts that have to be generated from the source fact d_i by + /// the data-flow solver. Each target fact that is generated will hold + /// after CallSite. + /// + /// The concrete implementation of f is depending on the analysis problem. + /// In the following, we present a brief, contrived example: + /// + /// f is applied to each data-flow fact d_i that holds before ExitInst. We + /// assume that f is implemented to produce the following outputs. + /// \code + /// f(0) -> {0} // pass as identity into the callee target + /// f(r) -> {x} // map return value to lhs variable at CallSite + /// f(q) -> {o} // map pointer-typed formal q to actual o + /// f(.) -> {} // kill all other facts that are not visible to + /// the + /// // caller + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o ... + /// + /// x = CalleeFun(o, ...) + /// +------------------+ + /// +--|---------------+ | + /// +--|--|------------+ | | + /// v v v ... | | | + /// 0 o x ... | | | + /// | | | + /// | | | + /// | | | + /// Ty CalleeFun(q, ...) | | | + /// | | | + /// 0 q r ... + /// + /// return r + /// \endcode + { + FFF.getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite) + } -> std::convertible_to; + + /// + /// Describes the data-flows alongsite a CallSite. + /// + /// This flow function usually passes all data-flow facts that are not + /// involved in the function call alongsite the CallSite. Data-flow facts + /// that are not actual parameters or passed by value, modifications to + /// those within a callee are not visible in the caller context, are + /// mostly passed as identity. The call-to-return flow function may also + /// be used to describe special semantics (cf. getCallFlowFunction()). + /// + /// The returned flow function implementation f + /// (FlowFunction::computeTargets()) is applied to each data-flow fact d_i + /// that holds right before the CallSite. f's return type is a set of + /// (target) facts that have to be generated from the source fact d_i by + /// the data-flow solver. Each target fact that is generated will hold + /// after CallSite. + /// + /// The concrete implementation of f is depending on the analysis problem. + /// In the following, we present a brief, contrived example: + /// + /// f is applied to each data-flow fact d_i that holds before CallSite. We + /// assume that f is implemented to produce the following outputs. + /// \code + /// f(0) -> {0} // pass lambda as identity alongsite the CallSite + /// f(o) -> {o} // assuming that o is passed by value, it is + /// passed + /// // alongsite the CallSite + /// f(p) -> {} // assuming that p is a pointer-typed value, we + /// need + /// // to kill p, as it will be handled by the call- + /// and + /// // return-flow functions + /// f(.) -> {.} // pass everything that is not involved in the + /// call + /// // as identity + /// \endcode + /// The above implementation corresponds to the following edges in the + /// exploded supergraph. + /// \code + /// 0 o ... + /// | | + /// | +-------+ + /// +--------+ | + /// | | + /// x = CalleeFun(o, p, ...) | | + /// | | + /// +--------+ | + /// | +-------+ + /// v v + /// 0 o x ... + /// \endcode + { + FFF.getCallToRetFlowFunction(CallSite, RetSite, Callees) + } -> std::convertible_to; + + /// + /// May be used to encode special sementics of a given callee target + /// (whose call should not be directly followed by the data-flow solver) + /// similar to the getCallFlowFunction() --> getCallToRetFlowFunction() + /// trick (cf. getCallFlowFunction()). + /// + /// The default implementation returns a nullptr to indicate that the + /// mechanism should not be used. + /// + { + FFF.getSummaryFlowFunction(Curr, CalleeFun) + } -> std::convertible_to; + }; + +template +concept UnbalancedRetSideEffectProvider = requires( + T &FFF, typename T::n_t ExitInst, typename T::n_t CallSite, + typename T::f_t CalleeFun, typename T::d_t Source) { + /// Performs any side-effects of a return-flow-function + /// + /// In case of unbalanced returns (if the option `followReturnsPastSeeds` is + /// activated in the IfdsIdeSolverConfig), we will eventually reach a function + /// that is not called from other functions. Still, we may want to apply a + /// return-flow-function -- just for its side-effects, such as registering a + /// taint + { + FFF.applyUnbalancedRetFlowFunctionSideEffects(CalleeFun, ExitInst, Source) + }; +}; + } // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/IDEProblem.h b/include/phasar/DataFlow/IfdsIde/IDEProblem.h new file mode 100644 index 0000000000..1211b62b2b --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IDEProblem.h @@ -0,0 +1,30 @@ +#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/DataFlow/IfdsIde/EdgeFunctions.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/SemiRing.h" + +namespace psr { +template +concept IDEProblem = + IFDSProblem && // + EdgeFunctionFactory && // + IsJoinLattice && // + IsSemiRing && // + requires { + requires IdeAnalysisDomain; + requires std::same_as; + requires std::same_as; + }; +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IDEProblemWrapper.h b/include/phasar/DataFlow/IfdsIde/IDEProblemWrapper.h new file mode 100644 index 0000000000..f2d69f0732 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IDEProblemWrapper.h @@ -0,0 +1,150 @@ +#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/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblemWrapper.h" +#include "phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/SemiRing.h" + +#include "llvm/ADT/ArrayRef.h" + +namespace psr { + +/// \brief A simple wrapper over a pointer to an IFDS/IDE problem. +/// +/// Useful for adding intermediate layers, e.g., IFDS->IDE translation, caching, +/// etc. +template +class IDEProblemWrapper : public IFDSProblemWrapper { + using base_t = IFDSProblemWrapper; + +public: + using ProblemAnalysisDomain = typename ProblemTy::ProblemAnalysisDomain; + using d_t = typename ProblemAnalysisDomain::d_t; + using n_t = typename ProblemAnalysisDomain::n_t; + using f_t = typename ProblemAnalysisDomain::f_t; + using t_t = typename ProblemAnalysisDomain::t_t; + using v_t = typename ProblemAnalysisDomain::v_t; + using l_t = typename ProblemAnalysisDomain::l_t; + using i_t = typename ProblemAnalysisDomain::i_t; + using db_t = typename ProblemAnalysisDomain::db_t; + + using base_t::base_t; + + // --- EdgeFunctionFactory: + + using EdgeFunctionType = typename ProblemTy::EdgeFunctionType; + + [[nodiscard]] constexpr decltype(auto) + getNormalEdgeFunction(ByConstRef Curr, ByConstRef CurrNode, + ByConstRef Succ, ByConstRef SuccNode) { + return this->Problem->getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); + } + + [[nodiscard]] constexpr decltype(auto) + getCallEdgeFunction(ByConstRef CallSite, ByConstRef CSNode, + ByConstRef CalleeFun, ByConstRef CalleeNode) { + return this->Problem->getCallEdgeFunction(CallSite, CSNode, CalleeFun, + CalleeNode); + } + + [[nodiscard]] constexpr decltype(auto) + getReturnEdgeFunction(ByConstRef CallSite, ByConstRef CalleeFun, + ByConstRef ExitInst, ByConstRef ExitNode, + ByConstRef RetSite, ByConstRef RSNode) { + return this->Problem->getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, + ExitNode, RetSite, RSNode); + } + + [[nodiscard]] constexpr decltype(auto) + getCallToRetEdgeFunction(ByConstRef CallSite, ByConstRef CSNode, + ByConstRef RetSite, ByConstRef RSNode, + llvm::ArrayRef Callees) { + return this->Problem->getCallToRetEdgeFunction(CallSite, CSNode, RetSite, + RSNode, Callees); + } + + [[nodiscard]] constexpr decltype(auto) + getSummaryEdgeFunction(ByConstRef Curr, ByConstRef CurrNode, + ByConstRef Succ, ByConstRef SuccNode) { + return this->Problem->getSummaryEdgeFunction(Curr, CurrNode, Succ, + SuccNode); + } + + // --- IsJoinLattice: + + [[nodiscard]] constexpr decltype(auto) topElement() { + return this->Problem->topElement(); + } + + [[nodiscard]] constexpr decltype(auto) bottomElement() { + return this->Problem->bottomElement(); + } + + [[nodiscard]] constexpr decltype(auto) join(auto &&L, auto &&R) { + return this->Problem->join(PSR_FWD(L), PSR_FWD(R)); + } + + // --- IsSemiRing: + + [[nodiscard]] constexpr decltype(auto) + extend(IsEdgeFunctionFor auto &&First, + IsEdgeFunctionFor auto &&Second) { + return this->Problem->extend(PSR_FWD(First), PSR_FWD(Second)); + } + + [[nodiscard]] constexpr decltype(auto) + combine(IsEdgeFunctionFor auto &&First, + IsEdgeFunctionFor auto &&Second) { + return this->Problem->combine(PSR_FWD(First), PSR_FWD(Second)); + } + + [[nodiscard]] constexpr decltype(auto) identity() { + return this->Problem->identity(); + } + + [[nodiscard]] constexpr decltype(auto) allTopFunction() + requires HasAllTopFunction + { + return this->Problem->allTopFunction(); + } +}; + +template +class IfdsIdeProblemWrapper + : public IfdsToIdeProblemAdapter> { +public: + using IfdsToIdeProblemAdapter< + IFDSProblemWrapper>::IfdsToIdeProblemAdapter; + + using IDEProblemTy = IfdsIdeProblemWrapper; + + [[nodiscard]] constexpr IDEProblem auto &ideProblem() noexcept { + return *this; + } +}; + +template +class IfdsIdeProblemWrapper : public IDEProblemWrapper { +public: + using IDEProblemWrapper::IDEProblemWrapper; + + using IDEProblemTy = ProblemTy; + + [[nodiscard]] constexpr IDEProblem auto &ideProblem() noexcept { + return this->base(); + } +}; + +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h index 80f80f2e6d..cdbacbe796 100644 --- a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h @@ -14,6 +14,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/EntryPointUtils.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" #include "phasar/DataFlow/IfdsIde/IfdsIdeDomain.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" @@ -40,24 +41,6 @@ namespace psr { struct HasNoConfigurationType; -template class AllTopFnProvider { -public: - virtual ~AllTopFnProvider() = default; - /// Returns an edge function that represents the top element of the analysis. - virtual EdgeFunction allTopFunction() = 0; -}; - -template - requires HasJoinLatticeTraits -class AllTopFnProvider { -public: - virtual ~AllTopFnProvider() = default; - /// Returns an edge function that represents the top element of the analysis. - virtual EdgeFunction allTopFunction() { - return AllTop{}; - } -}; - /// \brief The analysis problem interface for IDE problems (solvable by the /// IDESolver). Create a subclass from this and override all pure-virtual /// functions to create your own IDE analysis. @@ -69,8 +52,7 @@ template , public EdgeFunctions, public JoinLattice, - public SemiRing, - public AllTopFnProvider { + public SemiRing { public: using ProblemAnalysisDomain = AnalysisDomainTy; using d_t = typename AnalysisDomainTy::d_t; @@ -104,6 +86,8 @@ class IDETabulationProblem : public FlowFunctions, Printer(std::make_unique::type>()) { assert(IRDB != nullptr); + + static_assert(IDEProblem); } IDETabulationProblem(IDETabulationProblem &&) noexcept = default; diff --git a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h index 8f2046fcb0..a951232f43 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h @@ -40,45 +40,71 @@ enum class SolverConfigOptions : uint32_t { }; /// \brief Configuration options for the solving process of IFDS/IDE problems -struct IFDSIDESolverConfig { - IFDSIDESolverConfig() noexcept = default; - IFDSIDESolverConfig(SolverConfigOptions Options) noexcept; +class IFDSIDESolverConfig { +public: + constexpr IFDSIDESolverConfig() noexcept = default; + constexpr IFDSIDESolverConfig(SolverConfigOptions Options) noexcept + : Options(Options) {} /// Returns whether the solver should handle unbalanced returns (default: /// false) - [[nodiscard]] bool followReturnsPastSeeds() const; + [[nodiscard]] constexpr bool followReturnsPastSeeds() const noexcept { + return hasFlag(Options, SolverConfigOptions::FollowReturnsPastSeeds); + } /// Returns whether the solver should automatically insert an identityFlow /// propagation for the special zero value (default: true) - [[nodiscard]] bool autoAddZero() const; + [[nodiscard]] constexpr bool autoAddZero() const noexcept { + return hasFlag(Options, SolverConfigOptions::AutoAddZero); + } /// Returns whether the IDE solver should perform IDE's phase 2 (default: /// true). You may want to turn this off for IFDS analyses. - [[nodiscard]] bool computeValues() const; + [[nodiscard]] constexpr bool computeValues() const noexcept { + return hasFlag(Options, SolverConfigOptions::ComputeValues); + } /// Returns, whether the solver should record all ESG edges (default: false) /// \note This option may severly hurt the solver's performance - [[nodiscard]] bool recordEdges() const; + [[nodiscard]] constexpr bool recordEdges() const noexcept { + return hasFlag(Options, SolverConfigOptions::RecordEdges); + } /// Returns, whether the solver should emit the ESG as DOT graph on the /// command-line (default: false) - [[nodiscard]] bool emitESG() const; + [[nodiscard]] constexpr bool emitESG() const noexcept { + return hasFlag(Options, SolverConfigOptions::EmitESG); + } /// Currently unused - [[nodiscard]] bool computePersistedSummaries() const; + [[nodiscard]] constexpr bool computePersistedSummaries() const noexcept { + return hasFlag(Options, SolverConfigOptions::ComputePersistedSummaries); + } /// \see followReturnsPastSeeds - void setFollowReturnsPastSeeds(bool Set = true); + constexpr void setFollowReturnsPastSeeds(bool Set = true) { + setFlag(Options, SolverConfigOptions::FollowReturnsPastSeeds, Set); + } /// \see autoAddZero - void setAutoAddZero(bool Set = true); + constexpr void setAutoAddZero(bool Set = true) { + setFlag(Options, SolverConfigOptions::AutoAddZero, Set); + } /// \see computeValues - void setComputeValues(bool Set = true); + constexpr void setComputeValues(bool Set = true) { + setFlag(Options, SolverConfigOptions::ComputeValues, Set); + } /// \see recordEdges - void setRecordEdges(bool Set = true); + constexpr void setRecordEdges(bool Set = true) { + setFlag(Options, SolverConfigOptions::RecordEdges, Set); + } /// \see emitESG - void setEmitESG(bool Set = true); + constexpr void setEmitESG(bool Set = true) { + setFlag(Options, SolverConfigOptions::EmitESG, Set); + } /// \see computePersistedSummaries - void setComputePersistedSummaries(bool Set = true); + constexpr void setComputePersistedSummaries(bool Set = true) { + setFlag(Options, SolverConfigOptions::ComputePersistedSummaries, Set); + } - void setConfig(SolverConfigOptions Opt); + constexpr void setConfig(SolverConfigOptions Opt) { Options = Opt; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const IFDSIDESolverConfig &SC); + IFDSIDESolverConfig SC); private: SolverConfigOptions Options = diff --git a/include/phasar/DataFlow/IfdsIde/IFDSProblem.h b/include/phasar/DataFlow/IfdsIde/IFDSProblem.h new file mode 100644 index 0000000000..0a3c75a545 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IFDSProblem.h @@ -0,0 +1,57 @@ +#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/DB/ProjectIRDB.h" +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeDomain.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Utils/TypeTraits.h" + +#include + +namespace psr { +template +concept IFDSProblem = + FlowFunctionFactory && + requires(T &P, const T &CP, + typename T::ProblemAnalysisDomain::d_t FlowFact) { + typename T::ProblemAnalysisDomain; + requires IfdsAnalysisDomain; + + { CP.isZeroValue(FlowFact) } -> std::convertible_to; + { + CP.getZeroValue() + } -> std::convertible_to; + + { + P.initialSeeds() + } -> std::convertible_to< + InitialSeeds::l_t>>; + + { CP.getProjectIRDB() } -> ProjectIRDBConstPtr; + { CP.getEntryPoints() } -> is_iterable_over_v; + }; + +template +inline IFDSIDESolverConfig getProblemSolverConfig(ProblemTy &Problem) noexcept { + if constexpr (requires { Problem.getIFDSIDESolverConfig(); }) { + return Problem.getIFDSIDESolverConfig(); + } else { + return IFDSIDESolverConfig{}; + } +} + +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IFDSProblemWrapper.h b/include/phasar/DataFlow/IfdsIde/IFDSProblemWrapper.h new file mode 100644 index 0000000000..dc5d7920a0 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IFDSProblemWrapper.h @@ -0,0 +1,103 @@ +#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/DB/ProjectIRDB.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/NonNullPtr.h" + +#include "llvm/ADT/ArrayRef.h" + +namespace psr { + +/// \brief A simple wrapper over a pointer to an IFDS problem. +/// +template class IFDSProblemWrapper { +public: + using ProblemAnalysisDomain = typename ProblemTy::ProblemAnalysisDomain; + using d_t = typename ProblemAnalysisDomain::d_t; + using n_t = typename ProblemAnalysisDomain::n_t; + using f_t = typename ProblemAnalysisDomain::f_t; + using t_t = typename ProblemAnalysisDomain::t_t; + using v_t = typename ProblemAnalysisDomain::v_t; + using i_t = typename ProblemAnalysisDomain::i_t; + using db_t = typename ProblemAnalysisDomain::db_t; + + constexpr IFDSProblemWrapper(NonNullPtr Problem) noexcept + : Problem(Problem) {} + constexpr IFDSProblemWrapper(ProblemTy *Problem) noexcept + : Problem(Problem) {} + + // --- IFDSProblem: + + [[nodiscard]] constexpr bool isZeroValue(ByConstRef Fact) const { + return Problem->isZeroValue(Fact); + } + + [[nodiscard]] constexpr decltype(auto) getZeroValue() const { + return Problem->getZeroValue(); + } + + [[nodiscard]] constexpr decltype(auto) initialSeeds() { + return Problem->initialSeeds(); + } + + [[nodiscard]] constexpr ProjectIRDBConstPtr auto getProjectIRDB() const { + return Problem->getProjectIRDB(); + } + + [[nodiscard]] constexpr decltype(auto) getEntryPoints() const { + return Problem->getEntryPoints(); + } + + // --- FlowFunctionFactory: + + using container_type = typename ProblemTy::container_type; + using FlowFunctionPtrType = typename ProblemTy::FlowFunctionPtrType; + + [[nodiscard]] constexpr decltype(auto) + getNormalFlowFunction(ByConstRef Curr, ByConstRef Succ) { + return Problem->getNormalFlowFunction(Curr, Succ); + } + + [[nodiscard]] constexpr decltype(auto) + getCallFlowFunction(ByConstRef Curr, ByConstRef CalleeFun) { + return Problem->getCallFlowFunction(Curr, CalleeFun); + } + + [[nodiscard]] constexpr decltype(auto) + getRetFlowFunction(ByConstRef CallSite, ByConstRef CalleeFun, + ByConstRef ExitInst, ByConstRef RetSite) { + return Problem->getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] constexpr decltype(auto) + getCallToRetFlowFunction(ByConstRef CallSite, ByConstRef RetSite, + llvm::ArrayRef Callees) { + return Problem->getCallToRetFlowFunction(CallSite, RetSite, Callees); + } + + [[nodiscard]] constexpr decltype(auto) + getSummaryFlowFunction(ByConstRef Curr, ByConstRef CalleeFun) { + return Problem->getSummaryFlowFunction(Curr, CalleeFun); + } + + // --- + + /// The wrapped problem + [[nodiscard]] auto &base() noexcept { return *Problem; } + [[nodiscard]] const auto &base() const noexcept { return *Problem; } + +protected: + NonNullPtr Problem; +}; + +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IfdsIdeDomain.h b/include/phasar/DataFlow/IfdsIde/IfdsIdeDomain.h index 748a8a5360..4b27a33cb7 100644 --- a/include/phasar/DataFlow/IfdsIde/IfdsIdeDomain.h +++ b/include/phasar/DataFlow/IfdsIde/IfdsIdeDomain.h @@ -28,7 +28,7 @@ concept IsEdgeValue = std::is_move_constructible_v && psr::IsEqualityComparable; template -concept IfdsAnalysisDomain = IsAnalysisDomain && requires() { +concept IfdsAnalysisDomain = IsAnalysisDomain && requires { typename T::d_t; typename T::i_t; @@ -37,7 +37,7 @@ concept IfdsAnalysisDomain = IsAnalysisDomain && requires() { }; template -concept IdeAnalysisDomain = IfdsAnalysisDomain && requires() { +concept IdeAnalysisDomain = IfdsAnalysisDomain && requires { typename T::l_t; requires IsEdgeValue; }; diff --git a/include/phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h b/include/phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h new file mode 100644 index 0000000000..a6fcdb02d8 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h @@ -0,0 +1,124 @@ +#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/DataFlow/IfdsIde/EntryPointUtils.h" +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeDomain.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/NonNullPtr.h" +#include "phasar/Utils/SemiRing.h" + +#include + +namespace psr { +template > +class IfdsIdeProblemMixin + : protected FlowFunctionTemplates, + public DerivedJoinLattice< + typename detail::ValueDomainAdder::l_t>, + public DefaultSemiRing< + typename detail::ValueDomainAdder::l_t>, + public detail::ValueDomainAdder { +protected: + using FFTemplates = + FlowFunctionTemplates; + +public: + using ProblemAnalysisDomain = detail::ValueDomainAdder; + using typename FFTemplates::container_type; + using typename FFTemplates::FlowFunctionPtrType; + using typename ProblemAnalysisDomain::d_t; + using typename ProblemAnalysisDomain::db_t; + using typename ProblemAnalysisDomain::f_t; + using typename ProblemAnalysisDomain::i_t; + using typename ProblemAnalysisDomain::l_t; + using typename ProblemAnalysisDomain::n_t; + using typename ProblemAnalysisDomain::t_t; + using typename ProblemAnalysisDomain::v_t; + using EdgeFunctionType = EdgeFunction; + + [[nodiscard]] constexpr ByConstRef getZeroValue() const noexcept { + return ZeroValue; + } + + [[nodiscard]] constexpr bool + isZeroValue(ByConstRef Fact) const noexcept { + return Fact == ZeroValue; + } + + [[nodiscard]] LLVM_ATTRIBUTE_RETURNS_NONNULL constexpr const auto * + getProjectIRDB() const noexcept { + return IRDB.get(); + } + + [[nodiscard]] constexpr const auto &getEntryPoints() const noexcept { + return EntryPoints; + } + + [[nodiscard]] auto getSummaryFlowFunction(n_t /*CallSite*/, f_t /*DestFun*/) { + return nullptr; + } + + [[nodiscard]] auto getSummaryEdgeFunction(n_t /*Curr*/, + ByConstRef /*CurrNode*/, + n_t /*Succ*/, + ByConstRef /*SuccNode*/) { + return EdgeIdentity{}; + } + +protected: + constexpr IfdsIdeProblemMixin(NonNullPtr IRDB, + std::vector EntryPoints, + d_t ZeroValue) + : IRDB(IRDB), ZeroValue(std::move(ZeroValue)), + EntryPoints(std::move(EntryPoints)) {} + + typename FlowFunctions::FlowFunctionPtrType + generateFromZero(d_t FactToGenerate) { + return FFTemplates::generateFlow(std::move(FactToGenerate), getZeroValue()); + } + + /// Seeds that just start with ZeroValue and bottomElement() at the starting + /// points of each EntryPoint function. + /// Takes the __ALL__ EntryPoint into account. + [[nodiscard]] static InitialSeeds + createDefaultSeeds(auto &&Self) + requires std::is_nothrow_default_constructible_v< + typename AnalysisDomainTy::c_t> + { + static_assert(std::derived_from, + IfdsIdeProblemMixin>); + InitialSeeds Seeds; + typename AnalysisDomainTy::c_t C{}; + + addSeedsForStartingPoints(Self.getEntryPoints(), Self.getProjectIRDB(), C, + Seeds, Self.getZeroValue(), Self.bottomElement()); + + return Seeds; + } + +#if __cpp_explicit_this_parameter >= 202110L + [[nodiscard]] InitialSeeds + createDefaultSeeds(this auto &&Self) { + return createDefaultSeeds(PSR_FWD(Self)); + } +#endif + + NonNullPtr IRDB{}; + d_t ZeroValue{}; + std::vector EntryPoints; +}; +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h b/include/phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h new file mode 100644 index 0000000000..785dab2a38 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h @@ -0,0 +1,98 @@ +#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/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/Domain/BinaryDomain.h" + +#include + +namespace psr { +template class IfdsToIdeProblemAdapter : public Base { +public: + using Base::Base; + + using typename Base::d_t; + using typename Base::f_t; + using typename Base::n_t; + + using l_t = BinaryDomain; + using EdgeFunctionType = EdgeIdentity; + + [[nodiscard]] constexpr decltype(auto) + getNormalEdgeFunction(ByConstRef /*Curr*/, ByConstRef /*CurrNode*/, + ByConstRef /*Succ*/, + ByConstRef /*SuccNode*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr decltype(auto) + getCallEdgeFunction(ByConstRef /*CallSite*/, ByConstRef /*CSNode*/, + ByConstRef /*CalleeFun*/, + ByConstRef /*CalleeNode*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr decltype(auto) getReturnEdgeFunction( + ByConstRef /*CallSite*/, ByConstRef /*CalleeFun*/, + ByConstRef /*ExitInst*/, ByConstRef /*ExitNode*/, + ByConstRef /*RetSite*/, ByConstRef /*RSNode*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr decltype(auto) getCallToRetEdgeFunction( + ByConstRef /*CallSite*/, ByConstRef /*CSNode*/, + ByConstRef /*RetSite*/, ByConstRef /*RSNode*/, + llvm::ArrayRef /*Callees*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr decltype(auto) + getSummaryEdgeFunction(ByConstRef /*Curr*/, ByConstRef /*CurrNode*/, + ByConstRef /*Succ*/, + ByConstRef /*SuccNode*/) noexcept { + return EdgeFunctionType{}; + } + + // --- IsJoinLattice: + + [[nodiscard]] constexpr auto topElement() noexcept { + return std::integral_constant{}; + } + + [[nodiscard]] constexpr auto bottomElement() noexcept { + return std::integral_constant{}; + } + + [[nodiscard]] constexpr l_t join(l_t L, l_t R) noexcept { + if (L != R) { + return bottomElement(); + } + return L; + } + + // --- IsSemiRing: + + [[nodiscard]] constexpr auto extend(EdgeFunctionType /*First*/, + EdgeFunctionType /*Second*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr auto combine(EdgeFunctionType /*First*/, + EdgeFunctionType /*Second*/) noexcept { + return EdgeFunctionType{}; + } + + [[nodiscard]] constexpr auto identity() noexcept { + return EdgeFunctionType{}; + } +}; + +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/LegacyIDEProblemWrapper.h b/include/phasar/DataFlow/IfdsIde/LegacyIDEProblemWrapper.h new file mode 100644 index 0000000000..1cf4824e4d --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/LegacyIDEProblemWrapper.h @@ -0,0 +1,255 @@ +#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/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/NonNullPtr.h" +#include "phasar/Utils/PointerUtils.h" +#include "phasar/Utils/SemiRing.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" + +#include + +namespace psr { + +namespace detail { +/// \brief Holds everything that is common to both LegacyIDEProblemWrapper +/// specializations: the wrapped Problem pointer, the IFDSProblem/ +/// FlowFunctionFactory forwarding, and the legacy-base bookkeeping +/// (entry-points, solver-config, reports, soundness). +template +class LegacyIDEProblemWrapperBase : public LegacyBaseTy { +protected: + using base_t = LegacyBaseTy; + + static std::vector makeEntryVec(auto &&Range) { + if constexpr (std::is_convertible_v>) { + return PSR_FWD(Range); + } else { + return std::vector(llvm::adl_begin(Range), + llvm::adl_end(Range)); + } + } + +public: + using typename base_t::container_type; + using typename base_t::d_t; + using typename base_t::db_t; + using typename base_t::f_t; + using typename base_t::FlowFunctionPtrType; + using typename base_t::i_t; + using typename base_t::l_t; + using typename base_t::n_t; + using typename base_t::ProblemAnalysisDomain; + using typename base_t::t_t; + using typename base_t::v_t; + + LegacyIDEProblemWrapperBase(NonNullPtr Problem) noexcept + : base_t(getPointerFrom(Problem->getProjectIRDB()), + makeEntryVec(Problem->getEntryPoints()), + Problem->getZeroValue()), + Problem(Problem) { + this->getIFDSIDESolverConfig() = getProblemSolverConfig(*Problem); + } + + LegacyIDEProblemWrapperBase(ProblemTy *Problem) noexcept + : LegacyIDEProblemWrapperBase(NonNullPtr(Problem)) {} + + // --- IFDSProblem: + + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept final { + return Problem->isZeroValue(Fact); + } + + [[nodiscard]] InitialSeeds initialSeeds() final { + return Problem->initialSeeds(); + } + + void emitTextReport(GenericSolverResults Results, + llvm::raw_ostream &OS = llvm::outs()) final { + if constexpr (requires { Problem->emitTextReport(Results, OS); }) { + Problem->emitTextReport(Results, OS); + } else { + this->base_t::emitTextReport(Results, OS); + } + } + + void emitGraphicalReport(GenericSolverResults Results, + llvm::raw_ostream &OS = llvm::outs()) final { + if constexpr (requires { Problem->emitGraphicalReport(Results, OS); }) { + Problem->emitGraphicalReport(Results, OS); + } else { + this->base_t::emitGraphicalReport(Results, OS); + } + } + + bool setSoundness(Soundness S) final { + if constexpr (requires { Problem->setSoundness(S); }) { + return Problem->setSoundness(S); + } else { + return false; + } + } + + // --- FlowFunctionFactory: + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, + n_t Succ) final { + return Problem->getNormalFlowFunction(Curr, Succ); + } + + [[nodiscard]] FlowFunctionPtrType getCallFlowFunction(n_t Curr, + f_t CalleeFun) final { + return Problem->getCallFlowFunction(Curr, CalleeFun); + } + + [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, + f_t CalleeFun, + n_t ExitInst, + n_t RetSite) final { + return Problem->getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); + } + + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) final { + return Problem->getCallToRetFlowFunction(CallSite, RetSite, Callees); + } + + [[nodiscard]] FlowFunctionPtrType + getSummaryFlowFunction(n_t Curr, f_t CalleeFun) final { + return Problem->getSummaryFlowFunction(Curr, CalleeFun); + } + +protected: + NonNullPtr Problem; +}; +} // namespace detail + +/// \brief A simple wrapper over a pointer to an IFDS/IDE problem, implementing +/// the legacy IDETabulationProblem +template +class LegacyIDEProblemWrapper + : public detail::LegacyIDEProblemWrapperBase< + ProblemTy, + IFDSTabulationProblem> { + using base_t = detail::LegacyIDEProblemWrapperBase< + ProblemTy, + IFDSTabulationProblem>; + +public: + using base_t::base_t; +}; + +/// \brief A simple wrapper over a pointer to an IFDS/IDE problem, implementing +/// the legacy IDETabulationProblem +template +class LegacyIDEProblemWrapper + : public detail::LegacyIDEProblemWrapperBase< + ProblemTy, + IDETabulationProblem> { + using base_t = detail::LegacyIDEProblemWrapperBase< + ProblemTy, IDETabulationProblem>; + +public: + using base_t::base_t; + using base_t::Problem; + using typename base_t::d_t; + using typename base_t::f_t; + using typename base_t::l_t; + using typename base_t::n_t; + + // --- EdgeFunctionFactory: + + using EdgeFunctionType = EdgeFunction; + + [[nodiscard]] EdgeFunctionType + getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, d_t SuccNode) final { + return Problem->getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); + } + + [[nodiscard]] EdgeFunctionType getCallEdgeFunction(n_t CallSite, d_t CSNode, + f_t CalleeFun, + d_t CalleeNode) final { + return Problem->getCallEdgeFunction(CallSite, CSNode, CalleeFun, + CalleeNode); + } + + [[nodiscard]] EdgeFunctionType + getReturnEdgeFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, d_t ExitNode, + n_t RetSite, d_t RSNode) final { + return Problem->getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, + ExitNode, RetSite, RSNode); + } + + [[nodiscard]] EdgeFunctionType + getCallToRetEdgeFunction(n_t CallSite, d_t CSNode, n_t RetSite, d_t RSNode, + llvm::ArrayRef Callees) final { + return Problem->getCallToRetEdgeFunction(CallSite, CSNode, RetSite, RSNode, + Callees); + } + + [[nodiscard]] EdgeFunctionType + getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, d_t SuccNode) final { + return Problem->getSummaryEdgeFunction(Curr, CurrNode, Succ, SuccNode); + } + + // --- IsJoinLattice: + + [[nodiscard]] l_t topElement() final { return Problem->topElement(); } + + [[nodiscard]] l_t bottomElement() final { return Problem->bottomElement(); } + + [[nodiscard]] l_t join(l_t L, l_t R) final { + return Problem->join(std::move(L), std::move(R)); + } + + // --- IsSemiRing: + + [[nodiscard]] EdgeFunctionType extend(const EdgeFunctionType &First, + const EdgeFunctionType &Second) final { + return Problem->extend(First, Second); + } + + [[nodiscard]] EdgeFunctionType combine(const EdgeFunctionType &First, + const EdgeFunctionType &Second) final { + return Problem->combine(First, Second); + } + + [[nodiscard]] EdgeFunctionType identity() final { + return Problem->identity(); + } + + [[nodiscard]] EdgeFunctionType allTopFunction() final { + if constexpr (HasAllTopFunction) { + return Problem->allTopFunction(); + } else if constexpr (HasJoinLatticeTraits) { + return AllTop{}; + } else { + return AllTop{Problem->topElement()}; + } + } +}; + +} // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 7521fb865c..f93023c5d9 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -10,10 +10,10 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHE_H #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHE_H -#include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" -#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" #include "phasar/Utils/EquivalenceClassMap.h" +#include "phasar/Utils/Lazy.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/PAMMMacros.h" @@ -23,9 +23,8 @@ #include "llvm/ADT/DenseMap.h" #include +#include #include -#include -#include #include #include #include @@ -58,11 +57,8 @@ class LLVMMapKeyCompressor { using CompressedType = uint32_t; [[nodiscard]] CompressedType getCompressedID(KeyType Key) { - auto Search = Map.find(Key); - if (Search == Map.end()) { - return Map.insert(std::make_pair(Key, Map.size() + 1)).first->getSecond(); - } - return Search->getSecond(); + auto [It, _] = Map.try_emplace(Key, Map.size() + 1); + return It->second; } private: @@ -75,29 +71,27 @@ class LLVMMapKeyCompressor { * version is used if existend, otherwise a new one is created and inserted * into the cache. */ -template > -class FlowEdgeFunctionCache { - using IDEProblemType = IDETabulationProblem; - using FlowFunctionPtrType = typename IDEProblemType::FlowFunctionPtrType; +template class FlowEdgeFunctionCache { + using FlowFunctionPtrType = typename ProblemTy::FlowFunctionPtrType; - using n_t = typename AnalysisDomainTy::n_t; - using d_t = typename AnalysisDomainTy::d_t; - using f_t = typename AnalysisDomainTy::f_t; - using t_t = typename AnalysisDomainTy::t_t; - using l_t = typename AnalysisDomainTy::l_t; + using n_t = typename ProblemTy::n_t; + using d_t = typename ProblemTy::d_t; + using f_t = typename ProblemTy::f_t; + using t_t = typename ProblemTy::t_t; + using l_t = typename ProblemTy::l_t; + using container_type = typename ProblemTy::container_type; - using FlowFunctionType = FlowFunction; - using EdgeFunctionType = EdgeFunction; + using FlowFunctionType = FlowFunction; + using EdgeFunctionType = typename ProblemTy::EdgeFunctionType; public: // Ctor allows access to the IDEProblem in order to get access to flow and // edge function factory functions. - FlowEdgeFunctionCache( - IDETabulationProblem &Problem) + FlowEdgeFunctionCache(NonNullPtr Problem) : Problem(Problem), - AutoAddZero(Problem.getIFDSIDESolverConfig().autoAddZero()), - ZV(Problem.getZeroValue()) { + AutoAddZero(getProblemSolverConfig(*Problem).autoAddZero()), + ZV(Problem->getZeroValue()) { + PAMM_GET_INSTANCE; REG_COUNTER("Normal-FF Construction", 0, Full); REG_COUNTER("Normal-FF Cache Hit", 0, Full); @@ -130,15 +124,6 @@ class FlowEdgeFunctionCache { REG_COUNTER("Summary-EF Cache Hit", 0, Full); } - ~FlowEdgeFunctionCache() = default; - - FlowEdgeFunctionCache(const FlowEdgeFunctionCache &FEFC) = default; - FlowEdgeFunctionCache &operator=(const FlowEdgeFunctionCache &FEFC) = default; - - FlowEdgeFunctionCache(FlowEdgeFunctionCache &&FEFC) noexcept = default; - FlowEdgeFunctionCache & - operator=(FlowEdgeFunctionCache &&FEFC) noexcept = default; - [[nodiscard]] NonNullPtr getNormalFlowFunction(n_t Curr, n_t Succ) { assertNotNull(Curr); @@ -156,9 +141,9 @@ class FlowEdgeFunctionCache { auto &NormalFE = NormalFunctionCache[std::move(Key)]; if (!NormalFE.FlowFuncPtr) { INC_COUNTER("Normal-FF Construction", 1, Full); - auto FF = Problem.getNormalFlowFunction(Curr, Succ); + auto FF = Problem->getNormalFlowFunction(Curr, Succ); NormalFE.FlowFuncPtr = AutoAddZero - ? std::make_unique(std::move(FF), ZV) + ? &ZFFOwner.emplace_back(std::move(FF), ZV) : std::move(FF); PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { @@ -180,14 +165,16 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(F) Dest Fun : " << FToString(DestFun))); auto Key = std::tie(CallSite, DestFun); - auto [It, Inserted] = CallFlowFunctionCache.try_emplace(std::move(Key)); - if (Inserted) { - INC_COUNTER("Call-FF Construction", 1, Full); - auto FF = Problem.getCallFlowFunction(CallSite, DestFun); - It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) - : std::move(FF); - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - } else { + auto [It, Inserted] = CallFlowFunctionCache.try_emplace( + std::move(Key), lazy{[&] { + INC_COUNTER("Call-FF Construction", 1, Full); + auto FF = Problem->getCallFlowFunction(CallSite, DestFun); + auto Ret = AutoAddZero ? &ZFFOwner.emplace_back(std::move(FF), ZV) + : std::move(FF); + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + return Ret; + }}); + if (!Inserted) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("Call-FF Cache Hit", 1, Full); } @@ -210,16 +197,17 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite))); auto Key = std::tie(CallSite, CalleeFun, ExitInst, RetSite); - auto [It, Inserted] = ReturnFlowFunctionCache.try_emplace(std::move(Key)); - if (Inserted) { - INC_COUNTER("Return-FF Construction", 1, Full); - auto FF = - Problem.getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); - It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) - : std::move(FF); - - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - } else { + auto [It, Inserted] = ReturnFlowFunctionCache.try_emplace( + std::move(Key), lazy{[&] { + INC_COUNTER("Return-FF Construction", 1, Full); + auto FF = Problem->getRetFlowFunction(CallSite, CalleeFun, ExitInst, + RetSite); + auto Ret = AutoAddZero ? &ZFFOwner.emplace_back(std::move(FF), ZV) + : std::move(FF); + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + return Ret; + }}); + if (!Inserted) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("Return-FF Cache Hit", 1, Full); } @@ -242,15 +230,15 @@ class FlowEdgeFunctionCache { for (auto Callee : Callees) { PHASAR_LOG_LEVEL(DEBUG, " " << FToString(Callee)); };); - auto Key = std::tie(CallSite, RetSite); + auto Key = createEdgeFunctionInstKey(CallSite, RetSite); - auto [It, Inserted] = - CallToRetFlowFunctionCache.try_emplace(std::move(Key)); - if (Inserted) { + auto &CTRFE = NormalFunctionCache[std::move(Key)]; + if (!CTRFE.FlowFuncPtr) { INC_COUNTER("CallToRet-FF Construction", 1, Full); - auto FF = Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees); - It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) - : std::move(FF); + auto FF = Problem->getCallToRetFlowFunction(CallSite, RetSite, Callees); + CTRFE.FlowFuncPtr = AutoAddZero + ? &ZFFOwner.emplace_back(std::move(FF), ZV) + : std::move(FF); PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); } else { @@ -258,7 +246,7 @@ class FlowEdgeFunctionCache { INC_COUNTER("CallToRet-FF Cache Hit", 1, Full); } - return getPointerFrom(It->second); + return getPointerFrom(CTRFE.FlowFuncPtr); } /// \note Unlike the other get*FlowFunction methods, this returns a nullable @@ -276,7 +264,7 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); PHASAR_LOG_LEVEL(DEBUG, "(F) Dest Mthd : " << FToString(DestFun)); PHASAR_LOG_LEVEL(DEBUG, ' ')); - auto FF = Problem.getSummaryFlowFunction(CallSite, DestFun); + auto FF = Problem->getSummaryFlowFunction(CallSite, DestFun); return FF; } @@ -300,7 +288,7 @@ class FlowEdgeFunctionCache { [&] { INC_COUNTER("Normal-EF Construction", 1, Full); auto EF = - Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); + Problem->getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); return EF; }, @@ -330,14 +318,16 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Dest Node : " << DToString(DestNode))); auto Key = std::tie(CallSite, SrcNode, DestinationFunction, DestNode); - auto [It, Inserted] = CallEdgeFunctionCache.try_emplace(std::move(Key)); - if (Inserted) { - INC_COUNTER("Call-EF Construction", 1, Full); - It->second = Problem.getCallEdgeFunction(CallSite, SrcNode, - DestinationFunction, DestNode); + auto [It, Inserted] = CallEdgeFunctionCache.try_emplace( + std::move(Key), lazy{[&] { + INC_COUNTER("Call-EF Construction", 1, Full); + auto Ret = Problem->getCallEdgeFunction( + CallSite, SrcNode, DestinationFunction, DestNode); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - } else { + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + return Ret; + }}); + if (!Inserted) { INC_COUNTER("Call-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } @@ -366,13 +356,18 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetNode))); auto Key = std::tie(CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); - auto [It, Inserted] = ReturnEdgeFunctionCache.try_emplace(std::move(Key)); + auto [It, Inserted] = ReturnEdgeFunctionCache.try_emplace( + std::move(Key), lazy{[&] { + INC_COUNTER("Return-EF Construction", 1, Full); + auto Ret = Problem->getReturnEdgeFunction( + CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + return Ret; + }}); - if (Inserted) { - INC_COUNTER("Return-EF Construction", 1, Full); - It->second = Problem.getReturnEdgeFunction( - CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + if (!Inserted) { + INC_COUNTER("Return-EF Cache Hit", 1, Full); + PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << It->second); @@ -401,13 +396,13 @@ class FlowEdgeFunctionCache { }); EdgeFuncInstKey OuterMapKey = createEdgeFunctionInstKey(CallSite, RetSite); - auto &Outer = CallToRetEdgeFunctionCache[std::move(OuterMapKey)]; + auto &Outer = NormalFunctionCache[std::move(OuterMapKey)]; - auto Ret = Outer.getOrInsertLazy( + auto Ret = Outer.EdgeFunctionMap.getOrInsertLazy( std::move(createEdgeFunctionNodeKey(CallNode, RetSiteNode)), [&] { INC_COUNTER("CallToRet-EF Construction", 1, Full); - auto Ret = Problem.getCallToRetEdgeFunction( + auto Ret = Problem->getCallToRetEdgeFunction( CallSite, CallNode, RetSite, RetSiteNode, Callees); PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); return Ret; @@ -436,13 +431,15 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetSiteNode)); PHASAR_LOG_LEVEL(DEBUG, ' ')); auto Key = std::tie(CallSite, CallNode, RetSite, RetSiteNode); - auto [It, Inserted] = SummaryEdgeFunctionCache.try_emplace(std::move(Key)); - if (Inserted) { - INC_COUNTER("Summary-EF Construction", 1, Full); - It->second = Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, - RetSiteNode); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - } else { + auto [It, Inserted] = SummaryEdgeFunctionCache.try_emplace( + std::move(Key), lazy{[&] { + INC_COUNTER("Summary-EF Construction", 1, Full); + auto Ret = Problem->getSummaryEdgeFunction(CallSite, CallNode, + RetSite, RetSiteNode); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + return Ret; + }}); + if (!Inserted) { INC_COUNTER("Summary-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); } @@ -538,12 +535,6 @@ class FlowEdgeFunctionCache { std::invoke(Fn, EF, EdgeFunctionKind::Return); } - for (const auto &[Key, CTRFns] : CallToRetEdgeFunctionCache) { - for (const auto &[Set, EF] : CTRFns) { - std::invoke(Fn, EF, EdgeFunctionKind::CallToReturn); - } - } - for (const auto &[Key, EF] : SummaryEdgeFunctionCache) { std::invoke(Fn, EF, EdgeFunctionKind::Summary); } @@ -567,16 +558,11 @@ class FlowEdgeFunctionCache { std::is_base_of_v>, uint64_t, std::pair>; using InnerEdgeFunctionMapType = - EquivalenceClassMap; + EquivalenceClassMapNG; - using ZFF = ZeroedFlowFunction; + using ZFF = ZeroedFlowFunction; struct NormalEdgeFlowData { - NormalEdgeFlowData() noexcept = default; - NormalEdgeFlowData(FlowFunctionPtrType Val) : FlowFuncPtr(std::move(Val)) {} - NormalEdgeFlowData(InnerEdgeFunctionMapType Map) - : EdgeFunctionMap{std::move(Map)} {} - FlowFunctionPtrType FlowFuncPtr{}; InnerEdgeFunctionMapType EdgeFunctionMap{}; }; @@ -597,33 +583,35 @@ class FlowEdgeFunctionCache { Val |= KeyCompressor.getCompressedID(Rhs); return Val; } else { - return std::make_pair(Lhs, Rhs); + return std::make_pair(std::move(Lhs), std::move(Rhs)); } } MapKeyCompressorType KeyCompressor; - IDETabulationProblem &Problem; + NonNullPtr Problem; // Auto add zero - bool AutoAddZero; + bool AutoAddZero{}; d_t ZV; - // Caches for the flow/edge functions - std::map NormalFunctionCache; + std::deque ZFFOwner; + + // Caches for the normal/CTR flow/edge functions + // NOTE: The key-spaces for normal and CTR functions are disjoint. The solver + // will never call getNormal[...]Function on call-sites, or + // getCallToRet[...]Function on non-call-sites. So, we can use the same cache + // for both here + llvm::DenseMap NormalFunctionCache; // Caches for the flow functions std::map, FlowFunctionPtrType> CallFlowFunctionCache; std::map, FlowFunctionPtrType> ReturnFlowFunctionCache; - std::map, FlowFunctionPtrType> - CallToRetFlowFunctionCache; // Caches for the edge functions std::map, EdgeFunctionType> CallEdgeFunctionCache; std::map, EdgeFunctionType> ReturnEdgeFunctionCache; - std::map - CallToRetEdgeFunctionCache; std::map, EdgeFunctionType> SummaryEdgeFunctionCache; }; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h index bc9f71592d..17c121deec 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h @@ -1,6 +1,7 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHE_H #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHE_H +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/GenericFlowFunction.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h" #include "phasar/Utils/ByRef.h" @@ -20,15 +21,6 @@ namespace psr { namespace detail { -template -concept IsFlowFunction = requires(T &FF, D Fact) { - { FF.computeTargets(Fact) } -> is_iterable_over_v; -}; - -template -concept IsFlowFunctionPtr = requires(T FF, D Fact) { - { FF->computeTargets(Fact) } -> is_iterable_over_v; -}; template struct AutoAddZeroFF { FFTy FF; @@ -109,23 +101,18 @@ template struct FlowFunctionCacheBase { std::decay_t().getSummaryFlowFunction( std::declval(), std::declval()))>; - static_assert(detail::IsFlowFunction || - detail::IsFlowFunctionPtr); - static_assert(detail::IsFlowFunction || - detail::IsFlowFunctionPtr); - static_assert(detail::IsFlowFunction || - detail::IsFlowFunctionPtr); - static_assert(detail::IsFlowFunction || - detail::IsFlowFunctionPtr); - static_assert(detail::IsFlowFunction || - detail::IsFlowFunctionPtr || + static_assert(IsFlowFunctionOrFFPtrOf); + static_assert(IsFlowFunctionOrFFPtrOf); + static_assert(IsFlowFunctionOrFFPtrOf); + static_assert(IsFlowFunctionOrFFPtrOf); + static_assert(IsFlowFunctionOrFFPtrOf || std::is_same_v); template static constexpr bool needs_cache_v = std::is_same_v>, T> || std::is_same_v, T> || - (detail::IsFlowFunctionPtr && !std::is_pointer_v); + (IsFlowFunctionPtrOf && !std::is_pointer_v); }; } // namespace detail @@ -163,7 +150,7 @@ class FlowFunctionCache using ff_t = std::decay_t; if constexpr (AutoAddZero) { - if constexpr (detail::IsFlowFunctionPtr) { + if constexpr (IsFlowFunctionPtrOf) { if constexpr (needs_cache_v) { return detail::AutoAddZeroFF{ GenericFlowFunctionView(getPointerFrom(FF)), @@ -178,7 +165,7 @@ class FlowFunctionCache Problem.getZeroValue()}; } } else { - if constexpr (detail::IsFlowFunctionPtr) { + if constexpr (IsFlowFunctionPtrOf) { if constexpr (needs_cache_v) { return GenericFlowFunctionView(getPointerFrom(FF)); } else { diff --git a/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h index 4fa67d88fa..97c3253401 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h @@ -1,6 +1,7 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_GENERICSOLVERRESULTS_H #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_GENERICSOLVERRESULTS_H +#include "phasar/Domain/AnalysisDomain.h" #include "phasar/Domain/BinaryDomain.h" #include "phasar/Utils/ByRef.h" @@ -162,6 +163,11 @@ template class GenericSolverResults final { alignas(alignof(void *)) std::array Buffer{}; }; +template +using GenericSolverResultsFor = GenericSolverResults< + typename AnalysisDomainTy::n_t, typename AnalysisDomainTy::d_t, + typename detail::ValueDomainAdder::l_t>; + template bool ifdsEqual(const SR1 &LHS, const SR2 &RHS) { if (LHS.size() != RHS.size()) { diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 8a4c512b9c..26ddf0ed5a 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -25,9 +25,11 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/DataFlow/IfdsIde/LegacyIDEProblemWrapper.h" #include "phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/IfdsIde/Solver/IDESolverAPIMixin.h" @@ -41,6 +43,7 @@ #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Macros.h" +#include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/Nullable.h" #include "phasar/Utils/PAMMMacros.h" #include "phasar/Utils/Table.h" @@ -64,6 +67,23 @@ namespace psr { +namespace detail { + +template +class IDESolverProblemWrapperStorage { +protected: + constexpr IDESolverProblemWrapperStorage() = default; + + template + IDESolverProblemWrapperStorage(ProblemTy *Problem) + : ProblemOwner( + std::make_unique>(Problem)) {} + + std::unique_ptr> + ProblemOwner{}; +}; +} // namespace detail + /// Solves the given IDETabulationProblem as described in the 1996 paper by /// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results /// can then be queried by using resultAt() and resultsAt(). @@ -71,7 +91,9 @@ template , ICFG ICFGTy = typename AnalysisDomainTy::i_t> class IDESolver - : public IDESolverAPIMixin> { + : private detail::IDESolverProblemWrapperStorage, + public IDESolverAPIMixin> { friend IDESolverAPIMixin>; public: @@ -87,18 +109,36 @@ class IDESolver using t_t = typename AnalysisDomainTy::t_t; using v_t = typename AnalysisDomainTy::v_t; - IDESolver(IDETabulationProblem &Problem, - const ICFGTy *ICF) - : IDEProblem(Problem), ZeroValue(Problem.getZeroValue()), - ICF(&assertNotNull(ICF)), - SolverConfig(Problem.getIFDSIDESolverConfig()), - CachedFlowEdgeFunctions(Problem), AllTop(Problem.allTopFunction()), - JumpFn(std::make_unique>()), - Seeds(Problem.initialSeeds()) {} - IDESolver(IDETabulationProblem *Problem, const i_t *ICF) - : IDESolver(assertNotNull(Problem), ICF) {} + : IDEProblem(assertNotNull(Problem)), ZeroValue(Problem->getZeroValue()), + ICF(&assertNotNull(ICF)), + SolverConfig(Problem->getIFDSIDESolverConfig()), + CachedFlowEdgeFunctions(Problem), AllTop(Problem->allTopFunction()), + Seeds(Problem->initialSeeds()) {} + + template + [[deprecated( + "Use the other overload of IDESolver() instead, which takes the " + "ide-problem by pointer. This documents better that the solver captures " + "the address without taking ownership")]] IDESolver(ProblemTy &Problem, + const ICFGTy *ICF) + : IDESolver(&Problem, ICF) {} + + template + requires(!std::derived_from< + ProblemTy, + IDETabulationProblem>) + IDESolver(ProblemTy *Problem, const i_t *ICF) + : detail::IDESolverProblemWrapperStorage( + Problem), + IDEProblem(*this->ProblemOwner), ZeroValue(Problem->getZeroValue()), + ICF(&assertNotNull(ICF)), + SolverConfig(this->ProblemOwner->getIFDSIDESolverConfig()), + CachedFlowEdgeFunctions(this->ProblemOwner.get()), + AllTop(this->ProblemOwner->allTopFunction()), + Seeds(Problem->initialSeeds()) {} IDESolver(const IDESolver &) = delete; IDESolver &operator=(const IDESolver &) = delete; @@ -659,7 +699,7 @@ class IDESolver auto CallFlowFunction = CachedFlowEdgeFunctions.getCallFlowFunction(Stmt, Callee); INC_COUNTER("FF Queries", 1, Full); - for (const d_t dPrime : CallFlowFunction->computeTargets(Fact)) { + for (const d_t &dPrime : CallFlowFunction->computeTargets(Fact)) { auto EdgeFn = CachedFlowEdgeFunctions.getCallEdgeFunction( Stmt, Fact, Callee, dPrime); PHASAR_LOG_LEVEL(DEBUG, "Queried Call Edge Function: " << EdgeFn); @@ -1865,7 +1905,8 @@ class IDESolver size_t PathEdgeCount = 0; - FlowEdgeFunctionCache CachedFlowEdgeFunctions; + FlowEdgeFunctionCache> + CachedFlowEdgeFunctions; Table> ComputedIntraPathEdges; @@ -1873,7 +1914,8 @@ class IDESolver EdgeFunction AllTop; - std::unique_ptr> JumpFn; + std::unique_ptr> JumpFn = + std::make_unique>(); std::map, std::vector>> IntermediateEdgeFunctions; @@ -1914,12 +1956,11 @@ using IDESolver_P IDESolver; -template -OwningSolverResults -solveIDEProblem(IDETabulationProblem &Problem, - const ICFG auto &ICF) { +template +auto solveIDEProblem(ProblemTy &Problem, const ICFG auto &ICF) + -> OwningSolverResults { IDESolver Solver(&Problem, &ICF); Solver.solve(); return Solver.consumeSolverResults(); diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index de9e85be20..3b9462f5fb 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -17,6 +17,7 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_IFDSSOLVER_H #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IFDSSOLVER_H +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/Domain/BinaryDomain.h" @@ -50,18 +51,23 @@ class IFDSSolver : public IDESolver, using n_t = typename AnalysisDomainTy::n_t; using i_t = ICFGTy; - template - requires(std::same_as, - WithBinaryValueDomain>) - IFDSSolver(IFDSTabulationProblem &IFDSProblem, - const ICFGTy *ICF) + template + requires(std::same_as< + WithBinaryValueDomain, + WithBinaryValueDomain>) + [[deprecated( + "Use the other overload of IFDSSolver() instead, which takes the " + "ifds-problem by pointer. This documents better that the solver captures " + "the address without taking ownership")]] IFDSSolver(ProblemTy + &IFDSProblem, + const ICFGTy *ICF) : Base(IFDSProblem, ICF) {} - template - requires(std::same_as, - WithBinaryValueDomain>) - IFDSSolver(IFDSTabulationProblem *IFDSProblem, - const ICFGTy *ICF) + template + requires(std::same_as< + WithBinaryValueDomain, + WithBinaryValueDomain>) + IFDSSolver(ProblemTy *IFDSProblem, const ICFGTy *ICF) : Base(IFDSProblem, ICF) {} ~IFDSSolver() override = default; @@ -131,11 +137,11 @@ using IFDSSolver_P IFDSSolver; -template -OwningSolverResults -solveIFDSProblem(IFDSTabulationProblem &Problem, - const ICFG auto &ICF) { +template +auto solveIFDSProblem(ProblemTy &Problem, const ICFG auto &ICF) + -> OwningSolverResults { IFDSSolver Solver(&Problem, &ICF); Solver.solve(); return Solver.consumeSolverResults(); diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h index e2736176dd..4d2e83fac5 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -54,7 +54,7 @@ namespace psr { /// Applications: An Experience Report" /// () by Schiebel, Sattler, /// Schubert, Apel, and Bodden. -template , ICFG ICFGTy = typename ProblemTy::ProblemAnalysisDomain::i_t> class IterativeIDESolver @@ -65,10 +65,8 @@ class IterativeIDESolver std::conditional_t>, - public IterativeIDESolverBase< - StaticSolverConfigTy, - typename StaticSolverConfigTy::template EdgeFunctionPtrType< - typename ProblemTy::ProblemAnalysisDomain::l_t>>, + public IterativeIDESolverBase, public IDESolverAPIMixin< IterativeIDESolver> { @@ -89,10 +87,8 @@ class IterativeIDESolver using config_t = StaticSolverConfigTy; private: - using base_t = IterativeIDESolverBase< - StaticSolverConfigTy, - typename StaticSolverConfigTy::template EdgeFunctionPtrType< - typename domain_t::l_t>>; + using base_t = IterativeIDESolverBase; using base_results_t = detail::IterativeIDESolverResults; @@ -389,9 +385,9 @@ class IterativeIDESolver void submitInitialSeeds() { auto Seeds = Problem.initialSeeds(); - EdgeFunctionPtrType IdFun = [] { + EdgeFunctionPtrType IdFun = [&] { if constexpr (ComputeValues) { - return EdgeIdentity{}; + return Problem.identity(); } else { return EdgeFunctionPtrType{}; } @@ -848,9 +844,9 @@ class IterativeIDESolver combineIds(AtInstructionId, CalleeId)) .computeTargets(CSFact); - EdgeFunctionPtrType IdEF = [] { + EdgeFunctionPtrType IdEF = [&] { if constexpr (ComputeValues) { - return EdgeIdentity{}; + return Problem.identity(); } else { return EdgeFunctionPtrType{}; } diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h index e5a5a1b5b3..07f427aed4 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h @@ -27,8 +27,6 @@ class IterativeIDESolverBase { static constexpr bool ComputeValues = StaticSolverConfigTy::ComputeValues; static constexpr bool EnableStatistics = StaticSolverConfigTy::EnableStatistics; - /// NOTE: EdgeFunctionPtrType may be either std::unique_ptr> - /// or llvm::IntrusiveRefCntPtr> once this is supported using EdgeFunctionPtrType = std::conditional_t; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h index 5caede846d..2747c33833 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h @@ -16,6 +16,8 @@ #include "phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h" #include "phasar/Utils/Logger.h" +#include + namespace psr { template , @@ -31,11 +33,14 @@ class PathAwareIDESolver using i_t = typename base_t::i_t; using container_type = typename base_t::container_type; - explicit PathAwareIDESolver( - IDETabulationProblem *Problem, const i_t *ICF) + template + requires(std::same_as && + std::same_as) + explicit PathAwareIDESolver(ProblemTy *Problem, const i_t *ICF) : base_t(Problem, ICF), ESG(Problem->getZeroValue()) { - if (Problem->getIFDSIDESolverConfig().autoAddZero()) { + if (getProblemSolverConfig(*Problem).autoAddZero()) { PHASAR_LOG_LEVEL( WARNING, "The PathAwareIDESolver is initialized with the option 'autoAddZero' " @@ -43,8 +48,15 @@ class PathAwareIDESolver } } - explicit PathAwareIDESolver( - IDETabulationProblem &Problem, const i_t *ICF) + template + requires( + std::same_as && + std::same_as) + [[deprecated( + "Use the other overload of PathAwareIDESolver() instead, which takes the " + "ide-problem by pointer. This documents better that the solver " + "captures the address without taking ownership")]] + explicit PathAwareIDESolver(ProblemTy &Problem, const i_t *ICF) : PathAwareIDESolver(&Problem, ICF) {} [[nodiscard]] const ExplodedSuperGraph & diff --git a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h index e4fe5b6ca5..4f7aac8c71 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h @@ -63,9 +63,8 @@ using IDESolverConfig = WithComputeValues; using IFDSSolverConfig = WithComputeValues; template -struct [[clang::preferred_name(IDESolverConfig), - clang::preferred_name(IFDSSolverConfig)]] -WithComputeValues : Base { +struct PSR_PREFERRED_NAME(IDESolverConfig) + PSR_PREFERRED_NAME(IFDSSolverConfig) WithComputeValues : Base { static constexpr bool ComputeValues = ComputeValuesVal; }; @@ -74,8 +73,8 @@ using IDESolverConfigWithStats = WithStats; using IFDSSolverConfigWithStats = WithStats; template -struct [[clang::preferred_name(IDESolverConfigWithStats), - clang::preferred_name(IFDSSolverConfigWithStats)]] WithStats : Base { +struct PSR_PREFERRED_NAME(IDESolverConfigWithStats) + PSR_PREFERRED_NAME(IFDSSolverConfigWithStats) WithStats : Base { static constexpr bool EnableStatistics = EnableStats; }; @@ -93,8 +92,7 @@ template struct WithGCMode; using IFDSSolverConfigWithStatsAndGC = WithGCMode; template -struct [[clang::preferred_name(IFDSSolverConfigWithStatsAndGC)]] WithGCMode - : Base { +struct PSR_PREFERRED_NAME(IFDSSolverConfigWithStatsAndGC) WithGCMode : Base { static constexpr JumpFunctionGCMode EnableJumpFunctionGC = GCMode; }; diff --git a/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h index 900a532a07..d27120da00 100644 --- a/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h @@ -11,6 +11,9 @@ #include "phasar/ControlFlow/SparseCFGProvider.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IDEProblemWrapper.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/WPDS/RuleProvider.h" #include "phasar/Domain/BinaryDomain.h" @@ -35,7 +38,8 @@ template struct WeightTypeOf { }; } // namespace detail -template +template > class IfdsIdeRuleProvider { public: using control_location_type = typename ProblemT::d_t; @@ -209,14 +213,14 @@ class IfdsIdeRuleProvider { std::tuple> Outs; - for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { + for (const auto &[Inst, Facts] : Problem.initialSeeds().getSeeds()) { for (const auto &[Fact, Val] : Facts) { if constexpr (ComputeWeights) { using l_t = typename ProblemT::l_t; - if (Val == Problem->topElement()) { + if (Val == Problem.topElement()) { continue; } - if (Val == Problem->bottomElement()) { + if (Val == Problem.bottomElement()) { if constexpr (HasJoinLatticeTraits) { Outs.emplace_back(Fact, Inst, AllBottom{}); } else { @@ -248,17 +252,17 @@ class IfdsIdeRuleProvider { } } - ProblemT *Problem{}; + IfdsIdeProblemWrapper Problem; const ICFGTy *ICF{}; - FlowEdgeFunctionCache FECache{ - *Problem}; + FlowEdgeFunctionCache::IDEProblemTy> + FECache{&Problem.ideProblem()}; }; -template +template using IDERuleProvider = IfdsIdeRuleProvider; -template +template using IFDSRuleProvider = IfdsIdeRuleProvider; } // namespace psr::wpds diff --git a/include/phasar/Domain/AnalysisDomain.h b/include/phasar/Domain/AnalysisDomain.h index 03da68b9f6..a510ab284b 100644 --- a/include/phasar/Domain/AnalysisDomain.h +++ b/include/phasar/Domain/AnalysisDomain.h @@ -13,6 +13,7 @@ #include "phasar/ControlFlow/CFG.h" #include "phasar/Domain/IRDomain.h" +#include #include namespace psr { @@ -84,23 +85,32 @@ struct AnalysisDomain { enum class BinaryDomain; +template +concept HasValueDomain = requires() { + typename T::l_t; + requires(!std::is_void_v); +}; + namespace detail { -template -struct HasBinaryValueDomain : std::false_type {}; -template -struct HasBinaryValueDomain - : std::true_type {}; +template +concept HasBinaryValueDomain = + requires() { requires std::same_as; }; template struct WithBinaryValueDomainExtender : AnalysisDomainTy { using l_t = BinaryDomain; }; +template +using ValueDomainAdder = + std::conditional_t, AnalysisDomainTy, + WithBinaryValueDomainExtender>; + } // namespace detail template using WithBinaryValueDomain = - std::conditional_t::value, + std::conditional_t, AnalysisDomainTy, detail::WithBinaryValueDomainExtender>; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h index 9ed5419ced..2afe834223 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.h @@ -10,9 +10,8 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FIELDSENSALLOCSITESAWAREIFDSPROBLEM_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FIELDSENSALLOCSITESAWAREIFDSPROBLEM_H -#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" -#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" #include "phasar/Domain/BinaryDomain.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" @@ -23,6 +22,7 @@ #include "phasar/Utils/Fn.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/MapUtils.h" +#include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/SmallArraySet.h" #include "phasar/Utils/StrongTypeDef.h" #include "phasar/Utils/TableWrappers.h" @@ -227,7 +227,7 @@ struct AccessPathDMI { struct IFDSEdgeValue { using container_type = llvm::SmallDenseSet; - [[clang::require_explicit_initialization]] FieldStringManager *Mgr{}; + PSR_REQUIRE_EXPLICIT_INITIALIZATION FieldStringManager *Mgr{}; container_type Paths; static constexpr llvm::StringLiteral LogCategory = "IFDSEdgeValue"; @@ -289,14 +289,6 @@ struct IFDSProblemConfig : LLVMIFDSAnalysisDomainDefault { // XXX: more }; -/// Transforms user-defined seeds from usual IFDS seeds to field-sensitive IFDS -/// seeds -[[nodiscard]] InitialSeeds -makeInitialSeeds(const InitialSeeds &UserSeeds, - FieldStringManager &Mgr); - /// Utility to strip off potential pointer-arithmetic from V and accumulating /// the byte-offset. [[nodiscard]] inline std::pair @@ -394,8 +386,8 @@ bool filterFieldSensFacts( struct CFLFieldSensEdgeFunctionImpl { using l_t = LatticeDomain; - [[clang::require_explicit_initialization]] IFDSEdgeValue Transform; - [[clang::require_explicit_initialization]] uint8_t DepthKLimit{}; + PSR_REQUIRE_EXPLICIT_INITIALIZATION IFDSEdgeValue Transform; + PSR_REQUIRE_EXPLICIT_INITIALIZATION uint8_t DepthKLimit{}; bool operator==(const CFLFieldSensEdgeFunctionImpl &Other) const noexcept { assert(DepthKLimit == Other.DepthKLimit); @@ -432,7 +424,7 @@ struct CFLFieldSensEdgeFunctionImpl { struct CFLFieldSensEdgeFunction { using l_t = LatticeDomain; - [[clang::require_explicit_initialization]] const CFLFieldSensEdgeFunctionImpl + PSR_REQUIRE_EXPLICIT_INITIALIZATION const CFLFieldSensEdgeFunctionImpl *Impl{}; [[nodiscard]] l_t computeTarget(l_t Source) const { @@ -454,6 +446,86 @@ struct CFLFieldSensEdgeFunction { CFLFieldSensEdgeFunction EF); }; +class CFLFieldSensEdgeFunctions + : public IfdsIdeProblemMixin { +public: + static constexpr llvm::StringLiteral LogCategory = "CFLFieldSensIFDSProblem"; + + EdgeFunction getStoreEdgeFunction(d_t CurrNode, d_t SuccNode, + d_t PointerOp, d_t ValueOp, + uint8_t DepthKLimit, + const llvm::DataLayout &DL); + + EdgeFunction getLoadEdgeFunction(d_t CurrNode, d_t PointerOp, + uint8_t DepthKLimit, + const llvm::DataLayout &DL); + + EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, + d_t SuccNode); + + EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t DestinationFunction, d_t DestNode); + + EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, + n_t ExitStmt, d_t ExitNode, + n_t RetSite, d_t RetNode); + + EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t RetSite, d_t RetSiteNode, + llvm::ArrayRef Callees); + + EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, + d_t SuccNode); + + EdgeFunction extend(const EdgeFunction &L, + const EdgeFunction &R); + + EdgeFunction combine(const EdgeFunction &L, + const EdgeFunction &R); + +protected: + template + CFLFieldSensEdgeFunctions(ProblemTy &Problem, + cfl_fieldsens::IFDSProblemConfig &&Config, + uint8_t DepthKLimit = 5) + // entry-points not forwarded; getEntryPoints() overridden in + // CFLFieldSensIFDSProblem + : psr::IfdsIdeProblemMixin(Problem.getProjectIRDB(), {}, + Problem.getZeroValue()), + Config(std::move(Config)), DepthKLimit(DepthKLimit) { + Mgr.reserve(Problem.getProjectIRDB()->getNumInstructions()); + regCounters(); + } + + /// Transforms user-defined seeds from usual IFDS seeds to field-sensitive + /// IFDS seeds + [[nodiscard]] InitialSeeds + makeInitialSeeds(const InitialSeeds &UserSeeds); + +private: + using EFConstPtr = const cfl_fieldsens::CFLFieldSensEdgeFunctionImpl *; + using EFResultPtr = llvm::PointerIntPair; + + static void regCounters() noexcept; + + [[nodiscard]] EdgeFunction + makeEF(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF); + [[nodiscard]] EFResultPtr + makeEFPtr(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF); + + cfl_fieldsens::FieldStringManager Mgr{}; + cfl_fieldsens::IFDSProblemConfig Config{}; + + UnorderedSet EFInternCache{}; + + llvm::DenseMap, EFResultPtr> ExtendCache{}; + llvm::DenseMap, EFResultPtr> CombineCache{}; + + uint8_t DepthKLimit = 5; // Original from the paper +}; + } // namespace cfl_fieldsens /// An IFDS-Problem adaptor that makes any field-insensitive IFDS analysis @@ -462,22 +534,26 @@ struct CFLFieldSensEdgeFunction { /// /// The only thing to change in your usual IFDS problem is not to kill data-flow /// facts when only parts of the fields should be killed. This is now handled by -/// the CFLFieldSensIFDSProblem. For that, provide a CFLFieldSensIFDSProblem -/// with a proper KillsAt implementation. +/// the CFLFieldSensIFDSProblem. For that, provide a +/// problem definition with a proper killsAt() +/// implementation. +template + requires std::same_as class CFLFieldSensIFDSProblem - : public IDETabulationProblem { - using Base = IDETabulationProblem; + : public cfl_fieldsens::CFLFieldSensEdgeFunctions { + using Base = cfl_fieldsens::CFLFieldSensEdgeFunctions; static decltype(cfl_fieldsens::IFDSProblemConfig::KillsAt) - deriveKillsAt(auto *UserProblem) { + deriveKillsAt(ProblemTy *UserProblem) { assert(UserProblem != nullptr); - if constexpr (requires() { + if constexpr (requires { { UserProblem->killsAt() } -> psr::invocable_r, n_t, d_t>; }) { return UserProblem->killsAt(); - } else if constexpr (requires() { + } else if constexpr (requires { { UserProblem->killsAt() } -> std::invocable; @@ -504,29 +580,20 @@ class CFLFieldSensIFDSProblem using typename Base::t_t; using typename Base::v_t; - static constexpr llvm::StringLiteral LogCategory = "CFLFieldSensIFDSProblem"; - /// Constructs an IDETabulationProblem with the usual arguments, forwarded /// from UserProblem explicit CFLFieldSensIFDSProblem( - IFDSTabulationProblem *UserProblem, + ProblemTy *UserProblem, cfl_fieldsens::IFDSProblemConfig Config) noexcept(std::is_nothrow_move_constructible_v) - : Base(assertNotNull(UserProblem).getProjectIRDB(), - assertNotNull(UserProblem).getEntryPoints(), - UserProblem->getZeroValue()), - UserProblem(UserProblem), Config(std::move(Config)) { - Mgr.reserve(UserProblem->getProjectIRDB()->getNumInstructions()); - regCounters(); - } + : Base(assertNotNull(UserProblem), std::move(Config)), + UserProblem(UserProblem) {} /// Constructs an IDETabulationProblem with the usual arguments, forwarded /// from UserProblem and tries to automatically derive the config from /// additional functions specified by UserProblem - explicit CFLFieldSensIFDSProblem( - proper_subclass_of< - IFDSTabulationProblem> auto - *UserProblem) + + explicit CFLFieldSensIFDSProblem(ProblemTy *UserProblem) : CFLFieldSensIFDSProblem(UserProblem, cfl_fieldsens::IFDSProblemConfig{ .KillsAt = deriveKillsAt(UserProblem), @@ -537,101 +604,49 @@ class CFLFieldSensIFDSProblem CFLFieldSensIFDSProblem(std::nullptr_t) = delete; + [[nodiscard]] decltype(auto) getEntryPoints() const { + return UserProblem->getEntryPoints(); + } + // XXX: Perhaps we need a way to provide a customization-point to specify gen // offsets to the edge-functions (generating from zero currently always // generates at epsilon!) - [[nodiscard]] InitialSeeds initialSeeds() override { - return cfl_fieldsens::makeInitialSeeds(UserProblem->initialSeeds(), Mgr); + [[nodiscard]] InitialSeeds initialSeeds() { + return this->makeInitialSeeds(UserProblem->initialSeeds()); } - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { + [[nodiscard]] decltype(auto) getNormalFlowFunction(n_t Curr, n_t Succ) { return UserProblem->getNormalFlowFunction(Curr, Succ); } - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] decltype(auto) getCallFlowFunction(n_t CallInst, + f_t CalleeFun) { return UserProblem->getCallFlowFunction(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType - getSummaryFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] decltype(auto) getSummaryFlowFunction(n_t CallInst, + f_t CalleeFun) { return UserProblem->getSummaryFlowFunction(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { + [[nodiscard]] decltype(auto) getRetFlowFunction(n_t CallSite, f_t CalleeFun, + n_t ExitInst, n_t RetSite) { return UserProblem->getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); } - [[nodiscard]] FlowFunctionPtrType + [[nodiscard]] decltype(auto) getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { + llvm::ArrayRef Callees) { return UserProblem->getCallToRetFlowFunction(CallSite, RetSite, Callees); } - EdgeFunction getStoreEdgeFunction(d_t CurrNode, d_t SuccNode, - d_t PointerOp, d_t ValueOp, - uint8_t DepthKLimit, - const llvm::DataLayout &DL); - - EdgeFunction getLoadEdgeFunction(d_t CurrNode, d_t PointerOp, - uint8_t DepthKLimit, - const llvm::DataLayout &DL); - - EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; - - EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t DestinationFunction, - d_t DestNode) override; - - EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, - n_t ExitStmt, d_t ExitNode, - n_t RetSite, d_t RetNode) override; - - EdgeFunction - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, - d_t RetSiteNode, - llvm::ArrayRef Callees) override; - - EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; - - EdgeFunction extend(const EdgeFunction &L, - const EdgeFunction &R) override; - - EdgeFunction combine(const EdgeFunction &L, - const EdgeFunction &R) override; - /// The wrapped user-problem [[nodiscard]] const auto &base() const noexcept { return *UserProblem; } private: - using EFConstPtr = const cfl_fieldsens::CFLFieldSensEdgeFunctionImpl *; - using EFResultPtr = llvm::PointerIntPair; - - [[nodiscard]] EdgeFunction - makeEF(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF); - [[nodiscard]] EFResultPtr - makeEFPtr(cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF); - - static void regCounters() noexcept; - - IFDSTabulationProblem *UserProblem{}; - cfl_fieldsens::FieldStringManager Mgr{}; - cfl_fieldsens::IFDSProblemConfig Config{}; - - UnorderedSet EFInternCache{}; - - llvm::DenseMap, EFResultPtr> ExtendCache{}; - llvm::DenseMap, EFResultPtr> CombineCache{}; - - uint8_t DepthKLimit = 5; // Original from the paper + NonNullPtr UserProblem; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h index 800b996042..3c18f02a88 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -10,9 +10,8 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include @@ -62,14 +61,14 @@ class IDEAliasAwareDefaultFlowFunctionsImpl template class DefaultAliasAwareIDEProblem - : public IDETabulationProblem, + : public IfdsIdeProblemMixin, protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { public: - using typename IDETabulationProblem::db_t; - using typename IDETabulationProblem::n_t; - using typename IDETabulationProblem::f_t; - using typename IDETabulationProblem::d_t; - using typename IDETabulationProblem::FlowFunctionPtrType; + using typename IfdsIdeProblemMixin::db_t; + using typename IfdsIdeProblemMixin::n_t; + using typename IfdsIdeProblemMixin::f_t; + using typename IfdsIdeProblemMixin::d_t; + using typename IfdsIdeProblemMixin::FlowFunctionPtrType; using detail::IDEAliasAwareDefaultFlowFunctionsImpl::getAliasInfo; @@ -79,85 +78,37 @@ class DefaultAliasAwareIDEProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultAliasAwareIDEProblem( - const db_t *IRDB, LLVMAliasIteratorRef AS, - std::vector EntryPoints, - std::optional - ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IDETabulationProblem(IRDB, std::move(EntryPoints), - ZeroValue), - detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} - - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { - return getNormalFlowFunctionImpl(Curr, Succ); - } - - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { - return getCallFlowFunctionImpl(CallInst, CalleeFun); - } - - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { - return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); - } - - [[nodiscard]] FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { - return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); - } -}; - -class DefaultAliasAwareIFDSProblem - : public IFDSTabulationProblem, - protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { -public: - using typename IFDSTabulationProblem::d_t; - using typename IFDSTabulationProblem::f_t; - using typename IFDSTabulationProblem::FlowFunctionPtrType; - using typename IFDSTabulationProblem::n_t; - - /// Constructs an IFDSTabulationProblem with the usual arguments + alias - /// information. - /// - /// \note It is useful to use an instance of FilteredAliasSet for the alias - /// information to lower suprious aliases - explicit DefaultAliasAwareIFDSProblem( const db_t *IRDB, LLVMAliasIteratorRef AS, std::vector EntryPoints, d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), + : IfdsIdeProblemMixin(IRDB, std::move(EntryPoints), + std::move(ZeroValue)), detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} - using detail::IDEAliasAwareDefaultFlowFunctionsImpl::getAliasInfo; - - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { return getNormalFlowFunctionImpl(Curr, Succ); } - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] FlowFunctionPtrType getCallFlowFunction(n_t CallInst, + f_t CalleeFun) { return getCallFlowFunctionImpl(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { + [[nodiscard]] FlowFunctionPtrType + getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); } [[nodiscard]] FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { + llvm::ArrayRef Callees) { return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); } }; +using DefaultAliasAwareIFDSProblem = + DefaultAliasAwareIDEProblem; + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAllocSitesAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAllocSitesAwareIDEProblem.h index 0d67e364da..c9c77fc8e0 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAllocSitesAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAllocSitesAwareIDEProblem.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_DEFAULTALLOCSITESAWAREIDEPROBLEM_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMBasePointerAliasSet.h" @@ -68,7 +69,7 @@ class IDEAllocSitesAwareDefaultFlowFunctionsImpl template class DefaultAllocSitesAwareIDEProblem - : public IDETabulationProblem, + : public IfdsIdeProblemMixin, protected detail::IDEAllocSitesAwareDefaultFlowFunctionsImpl { public: using ProblemAnalysisDomain = AnalysisDomainTy; @@ -81,8 +82,6 @@ class DefaultAllocSitesAwareIDEProblem using i_t = typename AnalysisDomainTy::i_t; using db_t = typename AnalysisDomainTy::db_t; - using ConfigurationTy = HasNoConfigurationType; - using FlowFunctionType = FlowFunction; using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; @@ -94,86 +93,41 @@ class DefaultAllocSitesAwareIDEProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultAllocSitesAwareIDEProblem( - const db_t *IRDB, LLVMAliasInfoRef AS, - std::vector EntryPoints, - std::optional - ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IDETabulationProblem(IRDB, std::move(EntryPoints), - std::move(ZeroValue)), - detail::IDEAllocSitesAwareDefaultFlowFunctionsImpl(AS) {} - - void disableStrongUpdateStore() noexcept { - this->EnableStrongUpdateStore = false; - } - - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { - return getNormalFlowFunctionImpl(Curr, Succ); - } - - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { - return getCallFlowFunctionImpl(CallInst, CalleeFun); - } - - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { - return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); - } - - [[nodiscard]] FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { - return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); - } -}; - -class DefaultAllocSitesAwareIFDSProblem - : public IFDSTabulationProblem, - protected detail::IDEAllocSitesAwareDefaultFlowFunctionsImpl { -public: - /// Constructs an IFDSTabulationProblem with the usual arguments + alias - /// information. - /// - /// \note It is useful to use an instance of FilteredAliasSet for the alias - /// information to lower suprious aliases - explicit DefaultAllocSitesAwareIFDSProblem( const db_t *IRDB, LLVMAliasInfoRef AS, std::vector EntryPoints, d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), + : IfdsIdeProblemMixin(IRDB, std::move(EntryPoints), + std::move(ZeroValue)), detail::IDEAllocSitesAwareDefaultFlowFunctionsImpl(AS) {} void disableStrongUpdateStore() noexcept { this->EnableStrongUpdateStore = false; } - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { return getNormalFlowFunctionImpl(Curr, Succ); } - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] FlowFunctionPtrType getCallFlowFunction(n_t CallInst, + f_t CalleeFun) { return getCallFlowFunctionImpl(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { + [[nodiscard]] FlowFunctionPtrType + getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); } [[nodiscard]] FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { + llvm::ArrayRef Callees) { return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); } }; +using DefaultAllocSitesAwareIFDSProblem = + DefaultAllocSitesAwareIDEProblem; + } // namespace psr #endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_DEFAULTALLOCSITESAWAREIDEPROBLEM_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h index 63dcfbba78..7388e96b47 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h @@ -11,10 +11,7 @@ #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" -#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" namespace llvm { @@ -56,75 +53,40 @@ class IDENoAliasDefaultFlowFunctionsImpl { template class DefaultNoAliasIDEProblem - : public IDETabulationProblem, + : public IfdsIdeProblemMixin, protected detail::IDENoAliasDefaultFlowFunctionsImpl { public: - using IDETabulationProblem::IDETabulationProblem; + using IfdsIdeProblemMixin::IfdsIdeProblemMixin; - using typename IDETabulationProblem::f_t; - using typename IDETabulationProblem::FlowFunctionPtrType; - using typename IDETabulationProblem::n_t; + using typename IfdsIdeProblemMixin::f_t; + using typename IfdsIdeProblemMixin::FlowFunctionPtrType; + using typename IfdsIdeProblemMixin::n_t; + using typename IfdsIdeProblemMixin::d_t; - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { return getNormalFlowFunctionImpl(Curr, Succ); } - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] FlowFunctionPtrType getCallFlowFunction(n_t CallInst, + f_t CalleeFun) { return getCallFlowFunctionImpl(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { - return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); - } - [[nodiscard]] FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { - return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); - } -}; - -class DefaultNoAliasIFDSProblem - : public IFDSTabulationProblem, - protected detail::IDENoAliasDefaultFlowFunctionsImpl { -public: - using IFDSTabulationProblem::IFDSTabulationProblem; - - using typename IFDSTabulationProblem::d_t; - using typename IFDSTabulationProblem::f_t; - using typename IFDSTabulationProblem::FlowFunctionPtrType; - using typename IFDSTabulationProblem::l_t; - using typename IFDSTabulationProblem::n_t; - - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { - return getNormalFlowFunctionImpl(Curr, Succ); - } - - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { - return getCallFlowFunctionImpl(CallInst, CalleeFun); - } - - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { + getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); } [[nodiscard]] FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { + llvm::ArrayRef Callees) { return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); } }; +using DefaultNoAliasIFDSProblem = + DefaultNoAliasIDEProblem; + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h index 5101f6d728..db776694c2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h @@ -13,7 +13,7 @@ #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" -#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToInfo.h" // Forward declaration of types for which we only use its pointer or ref type @@ -64,14 +64,14 @@ class IDEReachableAllocationSitesDefaultFlowFunctionsImpl template class DefaultReachableAllocationSitesIDEProblem - : public IDETabulationProblem, + : public IfdsIdeProblemMixin, protected detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl { public: - using typename IDETabulationProblem::db_t; - using typename IDETabulationProblem::d_t; - using typename IDETabulationProblem::f_t; - using typename IDETabulationProblem::n_t; - using typename IDETabulationProblem::FlowFunctionPtrType; + using typename IfdsIdeProblemMixin::db_t; + using typename IfdsIdeProblemMixin::d_t; + using typename IfdsIdeProblemMixin::f_t; + using typename IfdsIdeProblemMixin::n_t; + using typename IfdsIdeProblemMixin::FlowFunctionPtrType; /// Constructs an IDETabulationProblem with the usual arguments + alias /// information. @@ -79,84 +79,37 @@ class DefaultReachableAllocationSitesIDEProblem /// \note It is useful to use an instance of FilteredAliasSet for the alias /// information to lower suprious aliases explicit DefaultReachableAllocationSitesIDEProblem( - const db_t *IRDB, LLVMPointsToIteratorRef AS, - std::vector EntryPoints, - std::optional - ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IDETabulationProblem(IRDB, std::move(EntryPoints), - ZeroValue), - detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl(AS) {} - - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { - return getNormalFlowFunctionImpl(Curr, Succ); - } - - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { - return getCallFlowFunctionImpl(CallInst, CalleeFun); - } - - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { - return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); - } - - [[nodiscard]] FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { - return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); - } -}; - -class DefaultReachableAllocationSitesIFDSProblem - : public IFDSTabulationProblem, - protected detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl { -public: - using typename IFDSTabulationProblem::d_t; - using typename IFDSTabulationProblem::db_t; - using typename IFDSTabulationProblem::f_t; - using typename IFDSTabulationProblem::FlowFunctionPtrType; - using typename IFDSTabulationProblem::n_t; - - /// Constructs an IFDSTabulationProblem with the usual arguments + alias - /// information. - /// - /// \note It is useful to use an instance of FilteredAliasSet for the alias - /// information to lower suprious aliases - explicit DefaultReachableAllocationSitesIFDSProblem( const db_t *IRDB, LLVMPointsToIteratorRef AS, std::vector EntryPoints, d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) - : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), + : IfdsIdeProblemMixin(IRDB, std::move(EntryPoints), + std::move(ZeroValue)), detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl(AS) {} - [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, - n_t Succ) override { + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { return getNormalFlowFunctionImpl(Curr, Succ); } - [[nodiscard]] FlowFunctionPtrType - getCallFlowFunction(n_t CallInst, f_t CalleeFun) override { + [[nodiscard]] FlowFunctionPtrType getCallFlowFunction(n_t CallInst, + f_t CalleeFun) { return getCallFlowFunctionImpl(CallInst, CalleeFun); } - [[nodiscard]] FlowFunctionPtrType getRetFlowFunction(n_t CallSite, - f_t CalleeFun, - n_t ExitInst, - n_t RetSite) override { + [[nodiscard]] FlowFunctionPtrType + getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { return getRetFlowFunctionImpl(CallSite, CalleeFun, ExitInst, RetSite); } [[nodiscard]] FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { + llvm::ArrayRef Callees) { return getCallToRetFlowFunctionImpl(CallSite, RetSite, Callees); } }; +using DefaultReachableAllocationSitesIFDSProblem = + DefaultReachableAllocationSitesIDEProblem; + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h index eda4bd6e4e..0d72760b8a 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h @@ -10,6 +10,9 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h" + #include "llvm/ADT/SmallPtrSet.h" namespace llvm { @@ -26,8 +29,9 @@ namespace psr::XTaint { class AnalysisBase { protected: const LLVMTaintConfig *TSF; + AbstractMemoryLocationFactory FactFactory; - explicit AnalysisBase(const LLVMTaintConfig *TSF) noexcept; + explicit AnalysisBase(const LLVMTaintConfig *TSF, size_t NumInstructions); using SourceConfigTy = llvm::SmallPtrSet; using SinkConfigTy = llvm::SmallPtrSet; @@ -51,6 +55,10 @@ class AnalysisBase { [[nodiscard]] SanitizerConfigTy getSanitizerConfigAt(const llvm::Instruction *Inst, const llvm::Function *Callee = nullptr) const; + + [[nodiscard]] AbstractMemoryLocation createZeroValue() const { + return FactFactory.getOrCreateZero(); + } }; } // namespace psr::XTaint diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h index f91866af7f..8f946fa6a2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h @@ -10,8 +10,9 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" -#include "phasar/Domain/LatticeDomain.h" +#include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" @@ -24,21 +25,17 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" #include "phasar/PhasarLLVM/Utils/BasicBlockOrdering.h" +#include "phasar/Utils/WithAnalysisPrinterMixin.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include #include -#include #include #include -#include namespace psr { @@ -53,21 +50,24 @@ namespace XTaint { /// \brief An IDE-based taint analysis that uses k-limited field-access paths to /// achieve field sensitivity class IDEExtendedTaintAnalysis - : public IDETabulationProblem, - public AnalysisBase { - using base_t = IDETabulationProblem; + : public AnalysisBase, + public IfdsIdeProblemMixin, + public WithAnalysisPrinterMixin { + using base_t = IfdsIdeProblemMixin; public: - using typename IDETabulationProblem::n_t; - using typename IDETabulationProblem::f_t; - using typename IDETabulationProblem::d_t; - using typename IDETabulationProblem::l_t; - using typename IDETabulationProblem< - IDEExtendedTaintAnalysisDomain>::FlowFunctionPtrType; + using typename base_t::d_t; + using typename base_t::f_t; + using typename base_t::FlowFunctionPtrType; + using typename base_t::l_t; + using typename base_t::n_t; using EdgeFunctionType = EdgeFunction; using config_callback_t = LLVMTaintConfig::TaintDescriptionCallBackTy; + // Need SemiRing::identity; below static identity() conflicts + using base_t::identity; + private: struct SourceSinkInfo { llvm::SmallBitVector SourceIndices, SinkIndices; @@ -93,12 +93,12 @@ class IDEExtendedTaintAnalysis const llvm::Instruction *CurrInst, bool AddGlobals = true); - [[nodiscard]] static inline bool equivalent(d_t LHS, d_t RHS) { + [[nodiscard]] static bool equivalent(d_t LHS, d_t RHS) { return LHS->equivalent(RHS); } - [[nodiscard]] static inline bool equivalentExceptPointerArithmetics(d_t LHS, - d_t RHS) { + [[nodiscard]] static bool equivalentExceptPointerArithmetics(d_t LHS, + d_t RHS) { return LHS->equivalentExceptPointerArithmetics(RHS); } @@ -181,105 +181,73 @@ class IDEExtendedTaintAnalysis std::vector EntryPoints, unsigned Bound, bool DisableStrongUpdates, GetDomTree &&GDT = DefaultDominatorTreeAnalysis{}) - : base_t(IRDB, std::move(EntryPoints), std::nullopt), AnalysisBase(TSF), - PT(PT), ICF(ICF), BBO(std::forward(GDT)), - FactFactory(IRDB->getNumInstructions()), + : AnalysisBase(TSF, IRDB->getNumInstructions()), + base_t(IRDB, std::move(EntryPoints), createZeroValue()), PT(PT), + ICF(ICF), BBO(std::forward(GDT)), DL(IRDB->getModule()->getDataLayout()), Bound(Bound), PostProcessed(DisableStrongUpdates), DisableStrongUpdates(DisableStrongUpdates) { assert(PT); assert(ICF != nullptr); - initializeZeroValue(createZeroValue()); FactFactory.setDataLayout(DL); - this->getIFDSIDESolverConfig().setAutoAddZero(false); + getIFDSIDESolverConfig().setAutoAddZero(false); /// TODO: Once we have better AliasInfo, do a dynamic_cast over PT and /// set HasPreciseAliasInfo accordingly } - ~IDEExtendedTaintAnalysis() override = default; - // Flow functions - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ); - FlowFunctionPtrType getCallFlowFunction(n_t CallStmt, f_t DestFun) override; + FlowFunctionPtrType getCallFlowFunction(n_t CallStmt, f_t DestFun); FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitStmt, n_t RetSite) override; + n_t ExitStmt, n_t RetSite); - FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override; + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees); - FlowFunctionPtrType getSummaryFlowFunction(n_t CallStmt, - f_t DestFun) override; + FlowFunctionPtrType getSummaryFlowFunction(n_t CallStmt, f_t DestFun); // Edge functions EdgeFunctionType getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); EdgeFunctionType getCallEdgeFunction(n_t CallInst, d_t SrcNode, f_t CalleeFun, - d_t DestNode) override; + d_t DestNode); EdgeFunctionType getReturnEdgeFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, d_t ExitNode, - n_t RetSite, d_t RetNode) override; + n_t RetSite, d_t RetNode); - EdgeFunctionType - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, - d_t RetSiteNode, - llvm::ArrayRef Callees) override; + EdgeFunctionType getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t RetSite, d_t RetSiteNode, + llvm::ArrayRef Callees); EdgeFunctionType getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); // Misc - InitialSeeds initialSeeds() override; + InitialSeeds initialSeeds(); - [[nodiscard]] d_t createZeroValue() const; - - [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept; // Printing functions void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override; - -private: - LLVMAliasInfoRef PT{}; - const LLVMBasedICFG *ICF{}; - - /// Save all leaks here that were found using the IFDS part if the analysis. - /// Hence, this map may contain sanitized facts. - XTaint::LeakMap_t Leaks; - - // Used for determining whether a dataflow fact is still tained or already - // sanitized - BasicBlockOrdering BBO; + llvm::raw_ostream &OS = llvm::outs()); - AbstractMemoryLocationFactory FactFactory; - const llvm::DataLayout &DL; - -#ifdef XTAINT_DIAGNOSTICS - llvm::DenseSet allTaintedValues; -#endif - - /// The k-limit for field-access paths - unsigned Bound; + // --- - /// Does the Leaks map still contain sanitized facts? - bool PostProcessed = false; - - bool DisableStrongUpdates = false; - - bool HasPreciseAliasInfo = false; + [[nodiscard]] IFDSIDESolverConfig &getIFDSIDESolverConfig() noexcept { + return SolverConfig; + } -public: BasicBlockOrdering &getBasicBlockOrdering() { return BBO; } /// Return a map from llvm::Instruction to sets of leaks (llvm::Values) that @@ -299,7 +267,7 @@ class IDEExtendedTaintAnalysis /// This function does NOT involve a post-processing step. LeakMap_t &getAllLeaks() { return Leaks; } - [[nodiscard]] inline size_t getNumDataflowFacts() const { + [[nodiscard]] size_t getNumDataflowFacts() const { return FactFactory.size(); } #ifdef XTAINT_DIAGNOSTICS @@ -309,6 +277,36 @@ class IDEExtendedTaintAnalysis return FactFactory.getNumOverApproximatedFacts(); } #endif + +private: + LLVMAliasInfoRef PT{}; + const LLVMBasedICFG *ICF{}; + + /// Save all leaks here that were found using the IFDS part if the analysis. + /// Hence, this map may contain sanitized facts. + XTaint::LeakMap_t Leaks; + + // Used for determining whether a dataflow fact is still tained or already + // sanitized + BasicBlockOrdering BBO; + + const llvm::DataLayout &DL; + +#ifdef XTAINT_DIAGNOSTICS + llvm::DenseSet allTaintedValues; +#endif + + IFDSIDESolverConfig SolverConfig{}; + + /// The k-limit for field-access paths + unsigned Bound; + + /// Does the Leaks map still contain sanitized facts? + bool PostProcessed = false; + + bool DisableStrongUpdates = false; + + bool HasPreciseAliasInfo = false; }; } // namespace XTaint diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h index f1e28fb426..b56095e03d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h @@ -13,6 +13,7 @@ #include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -79,22 +80,18 @@ struct IDEFeatureTaintEdgeFact { /// Checks whether this set contains no facts. /// Note: Top also counts as empty - [[nodiscard]] inline bool empty() const noexcept { - return isTop() || Taints.none(); - } + [[nodiscard]] bool empty() const noexcept { return isTop() || Taints.none(); } /// Returns the number of facts in this set. /// Note: Top counts as empty - [[nodiscard]] inline size_t size() const noexcept { + [[nodiscard]] size_t size() const noexcept { if (isTop()) { return 0; } return Taints.count(); } - [[nodiscard]] inline bool isBottom() const noexcept { return Taints.empty(); } - [[nodiscard]] inline bool isTop() const noexcept { - return Taints.isInvalid(); - } + [[nodiscard]] bool isBottom() const noexcept { return Taints.empty(); } + [[nodiscard]] bool isTop() const noexcept { return Taints.isInvalid(); } [[nodiscard]] friend llvm::hash_code hash_value(const IDEFeatureTaintEdgeFact &BV) noexcept { @@ -170,16 +167,16 @@ struct IDEFeatureTaintEdgeFact { [[nodiscard]] std::string LToString(const IDEFeatureTaintEdgeFact &EdgeFact); template <> struct JoinLatticeTraits { - inline static IDEFeatureTaintEdgeFact top() { + static IDEFeatureTaintEdgeFact top() { IDEFeatureTaintEdgeFact Ret{}; return Ret; } - inline static IDEFeatureTaintEdgeFact bottom() { + static IDEFeatureTaintEdgeFact bottom() { // TODO return 0; } - inline static IDEFeatureTaintEdgeFact join(const IDEFeatureTaintEdgeFact &L, - const IDEFeatureTaintEdgeFact &R) { + static IDEFeatureTaintEdgeFact join(const IDEFeatureTaintEdgeFact &L, + const IDEFeatureTaintEdgeFact &R) { if (L.isTop()) { return R; } @@ -273,7 +270,7 @@ class FeatureTaintGenerator { }; class IDEFeatureTaintAnalysis - : public IDETabulationProblem { + : public IfdsIdeProblemMixin { public: IDEFeatureTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, @@ -306,62 +303,56 @@ class IDEFeatureTaintAnalysis operator=(IDEFeatureTaintAnalysis &&) noexcept = delete; // The EF Caches are incomplete, so move the dtor into the .cpp - ~IDEFeatureTaintAnalysis() override; + ~IDEFeatureTaintAnalysis(); ////////////////////////////////////////////////////////////////////////////// /// Flow Functions ////////////////////////////////////////////////////////////////////////////// - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ); - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override; + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun); FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitInst, n_t RetSite) override; - FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override; - FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, - f_t DestFun) override; + n_t ExitInst, n_t RetSite); + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees); + FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun); ////////////////////////////////////////////////////////////////////////////// /// Edge Functions ////////////////////////////////////////////////////////////////////////////// EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t DestinationFunction, - d_t DestNode) override; + f_t DestinationFunction, d_t DestNode); EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, n_t ExitStmt, d_t ExitNode, - n_t RetSite, d_t RetNode) override; + n_t RetSite, d_t RetNode); - EdgeFunction - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, - d_t RetSiteNode, - llvm::ArrayRef Callees) override; + EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t RetSite, d_t RetSiteNode, + llvm::ArrayRef Callees); EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); ////////////////////////////////////////////////////////////////////////////// /// Misc ////////////////////////////////////////////////////////////////////////////// - InitialSeeds initialSeeds() override; - - bool isZeroValue(d_t FlowFact) const noexcept override; + InitialSeeds initialSeeds(); void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override; + llvm::raw_ostream &OS = llvm::outs()); EdgeFunction extend(const EdgeFunction &FirstEF, - const EdgeFunction &SecondEF) override; + const EdgeFunction &SecondEF); EdgeFunction combine(const EdgeFunction &FirstEF, - const EdgeFunction &OtherEF) override; + const EdgeFunction &OtherEF); private: FeatureTaintGenerator TaintGen; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h index 533b455f3a..8ca5d6955f 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h @@ -13,8 +13,9 @@ #include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" -#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" @@ -24,7 +25,6 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" -#include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/BitVectorSet.h" @@ -34,7 +34,6 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Twine.h" -#include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" @@ -45,15 +44,12 @@ #include "llvm/IR/Instructions.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" #include #include -#include -#include #include #include #include @@ -130,13 +126,12 @@ class IDEIIAFlowFact { /// location than the other one. [[nodiscard]] bool flowFactEqual(const IDEIIAFlowFact &Other) const; - [[nodiscard]] inline const llvm::Value *getBase() const { return BaseVal; } - [[nodiscard]] inline llvm::SmallVector + [[nodiscard]] const llvm::Value *getBase() const { return BaseVal; } + [[nodiscard]] llvm::SmallVector getField() const { return FieldDesc; } - [[nodiscard]] inline constexpr unsigned getKLimit() const { return KLimit; } + [[nodiscard]] constexpr unsigned getKLimit() const { return KLimit; } void print(llvm::raw_ostream &OS, bool IsForDebug = false) const; @@ -146,7 +141,7 @@ class IDEIIAFlowFact { bool operator==(const llvm::Value *V) const; - inline operator const llvm::Value *() { return BaseVal; } + operator const llvm::Value *() const { return BaseVal; } [[nodiscard]] std::string str() const { std::string Ret; @@ -210,15 +205,15 @@ struct IDEInstInteractionAnalysisDomain : public LLVMAnalysisDomainDefault { template class IDEInstInteractionAnalysisT - : public IDETabulationProblem< + : public IfdsIdeProblemMixin< IDEInstInteractionAnalysisDomain> { - using IDETabulationProblem< + using IfdsIdeProblemMixin< IDEInstInteractionAnalysisDomain>::generateFromZero; public: using AnalysisDomainTy = IDEInstInteractionAnalysisDomain; - using IDETabProblemType = IDETabulationProblem; + using IDETabProblemType = IfdsIdeProblemMixin; using typename IDETabProblemType::container_type; using typename IDETabProblemType::FlowFunctionPtrType; @@ -240,7 +235,7 @@ class IDEInstInteractionAnalysisT const LLVMProjectIRDB *IRDB, const LLVMBasedICFG *ICF, LLVMAliasInfoRef PT, std::vector EntryPoints = {"main"}, std::function EdgeFactGenerator = nullptr) - : IDETabulationProblem( + : IfdsIdeProblemMixin( IRDB, std::move(EntryPoints), createZeroValue()), ICF(ICF), PT(PT), EdgeFactGen(std::move(EdgeFactGenerator)) { assert(ICF != nullptr); @@ -249,19 +244,17 @@ class IDEInstInteractionAnalysisT // IIAAKillOrReplaceEF::initEdgeFunctionCleaner(); } - ~IDEInstInteractionAnalysisT() override = default; - /// Offer a special hook to the user that allows to generate additional /// edge facts on-the-fly. Above the generator function, the ordinary /// edge facts are generated according to the usual edge functions. - inline void registerEdgeFactGenerator( + void registerEdgeFactGenerator( std::function EdgeFactGenerator) { EdgeFactGen = std::move(EdgeFactGenerator); } // start formulating our analysis by specifying the parts required for IFDS - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t /* Succ */) override { + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t /* Succ */) { // Generate all local variables // // Flow function: @@ -483,8 +476,7 @@ class IDEInstInteractionAnalysisT }); } - inline FlowFunctionPtrType getCallFlowFunction(n_t CallSite, - f_t DestFun) override { + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) { if (this->ICF->isHeapAllocatingFunction(DestFun)) { // Kill add facts and model the effects in getCallToRetFlowFunction(). return this->killAllFlows(); @@ -521,9 +513,8 @@ class IDEInstInteractionAnalysisT return MapFactsToCalleeFF; } - inline FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t /*CalleeFun*/, - n_t ExitInst, - n_t /* RetSite */) override { + FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t /*CalleeFun*/, + n_t ExitInst, n_t /* RetSite */) { // Map return value back to the caller. If pointer parameters hold at the // end of a callee function generate all of those in the caller context. if (CallSite == nullptr) { @@ -546,9 +537,8 @@ class IDEInstInteractionAnalysisT return MapFactsToCallerFF; } - inline FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t /* RetSite */, - llvm::ArrayRef Callees) override { + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t /* RetSite */, + llvm::ArrayRef Callees) { // Model call to heap allocating functions (new, new[], malloc, etc.) -- // only model direct calls, though. if (Callees.size() == 1) { @@ -619,13 +609,7 @@ class IDEInstInteractionAnalysisT }); } - inline FlowFunctionPtrType - getSummaryFlowFunction(n_t /* CallSite */, f_t /* DestFun */) override { - // Do not use user-crafted summaries. - return nullptr; - } - - inline InitialSeeds initialSeeds() override { + InitialSeeds initialSeeds() { InitialSeeds Seeds; forallStartingPoints(this->EntryPoints, ICF, [this, &Seeds](n_t SP) { @@ -653,20 +637,19 @@ class IDEInstInteractionAnalysisT return Seeds; } - [[nodiscard]] inline d_t createZeroValue() const { + [[nodiscard]] d_t createZeroValue() const { // Create a special value to represent the zero value! return LLVMZeroValue::getInstance(); } - inline bool isZeroValue(d_t d) const noexcept override { - return isZeroValueImpl(d); - } + using IfdsIdeProblemMixin< + IDEInstInteractionAnalysisDomain>::isZeroValue; // In addition provide specifications for the IDE parts. - inline EdgeFunctionType getStrongUpdateStoreEF(const llvm::StoreInst *Store, - d_t CurrNode, d_t SuccNode, - l_t UserEdgeFacts) { + EdgeFunctionType getStrongUpdateStoreEF(const llvm::StoreInst *Store, + d_t CurrNode, d_t SuccNode, + l_t UserEdgeFacts) { // Overriding edge: obtain labels from value to be stored (and may add // UserEdgeFacts, if any). @@ -762,9 +745,8 @@ class IDEInstInteractionAnalysisT "\n> CurrNode: " + CurrNode.str() + "\n> SuccNode: " + SuccNode.str()); } - inline EdgeFunctionType getNormalEdgeFunction(n_t Curr, d_t CurrNode, - n_t /* Succ */, - d_t SuccNode) override { + EdgeFunctionType getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t /* Succ */, + d_t SuccNode) { PHASAR_LOG_LEVEL(DFADEBUG, "Process edge: " << llvmIRToShortString(CurrNode) << " --" << llvmIRToString(Curr) << "--> " @@ -814,9 +796,9 @@ class IDEInstInteractionAnalysisT return EdgeIdentity{}; } - inline EdgeFunctionType getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t /* DestinationMethod */, - d_t DestNode) override { + EdgeFunctionType getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t /* DestinationMethod */, + d_t DestNode) { // Handle the case in which a parameter that has been artificially // introduced by the compiler is passed. Such a value must be generated from // the zero value, to reflact the fact that the data flows from the callee @@ -848,9 +830,9 @@ class IDEInstInteractionAnalysisT return EdgeIdentity{}; } - inline EdgeFunctionType - getReturnEdgeFunction(n_t CallSite, f_t /* CalleeMethod */, n_t ExitInst, - d_t ExitNode, n_t /* RetSite */, d_t RetNode) override { + EdgeFunctionType getReturnEdgeFunction(n_t CallSite, f_t /* CalleeMethod */, + n_t ExitInst, d_t ExitNode, + n_t /* RetSite */, d_t RetNode) { // Handle the case in which constant data is returned, e.g. ret i32 42. // // Let c be the return instruction's corresponding call site. @@ -879,10 +861,9 @@ class IDEInstInteractionAnalysisT return EdgeIdentity{}; } - inline EdgeFunctionType - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t /* RetSite */, - d_t RetSiteNode, - llvm::ArrayRef Callees) override { + EdgeFunctionType getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t /* RetSite */, d_t RetSiteNode, + llvm::ArrayRef Callees) { // Check if the user has registered a fact generator function l_t UserEdgeFacts = bvSetFrom(invoke_or_default(EdgeFactGen, CallSite)); @@ -900,7 +881,7 @@ class IDEInstInteractionAnalysisT // // 0 // \ - // %i = call H \ \x.x \cup { commit of('%i = call H') } + // %i = call H \ \x.x \cup { commit of('%i = call H') } // v // i // @@ -933,15 +914,10 @@ class IDEInstInteractionAnalysisT return EdgeIdentity{}; } - inline EdgeFunctionType - getSummaryEdgeFunction(n_t /* CallSite */, d_t /* CallNode */, - n_t /* RetSite */, d_t /* RetSiteNode */) override { - // Do not use user-crafted summaries. - return nullptr; + [[nodiscard]] l_t join(const l_t &Lhs, const l_t &Rhs) { + return joinImpl(Lhs, Rhs); } - inline l_t join(l_t Lhs, l_t Rhs) override { return joinImpl(Lhs, Rhs); } - // Provide some handy helper edge functions to improve reuse. // Edge function that kills all labels in a set (and may replaces them with @@ -1028,7 +1004,7 @@ class IDEInstInteractionAnalysisT } EdgeFunction extend(const EdgeFunction &FirstFunction, - const EdgeFunction &SecondFunction) override { + const EdgeFunction &SecondFunction) { if (auto Default = defaultComposeOrNull(FirstFunction, SecondFunction)) { return Default; } @@ -1048,7 +1024,7 @@ class IDEInstInteractionAnalysisT } EdgeFunction combine(const EdgeFunction &FirstFunction, - const EdgeFunction &OtherFunction) override { + const EdgeFunction &OtherFunction) { /// XXX: Here, we underapproximate joins with EdgeIdentity if (llvm::isa>(FirstFunction)) { return OtherFunction; @@ -1086,7 +1062,7 @@ class IDEInstInteractionAnalysisT } void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override { + llvm::raw_ostream &OS = llvm::outs()) { OS << "\n====================== IDE-Inst-Interaction-Analysis Report " "======================\n"; // if (!IRDB->debugInfoAvailable()) { @@ -1120,7 +1096,7 @@ class IDEInstInteractionAnalysisT /// Computes all variables where a result set has been computed using the /// edge functions (and respective value domain). - inline std::unordered_set + [[nodiscard]] std::unordered_set getAllVariables(GenericSolverResults /* Solution */) const { std::unordered_set Variables; // collect all variables that are available @@ -1145,7 +1121,7 @@ class IDEInstInteractionAnalysisT /// Computes all variables for which an empty set has been computed using the /// edge functions (and respective value domain). - inline std::unordered_set getAllVariablesWithEmptySetValue( + [[nodiscard]] std::unordered_set getAllVariablesWithEmptySetValue( GenericSolverResults Solution) const { return removeVariablesWithoutEmptySetValue(Solution, getAllVariables(Solution)); @@ -1170,7 +1146,7 @@ class IDEInstInteractionAnalysisT } } - static inline l_t joinImpl(ByConstRef Lhs, ByConstRef Rhs) { + static l_t joinImpl(ByConstRef Lhs, ByConstRef Rhs) { if (Lhs.isTop() || Rhs.isBottom()) { return Rhs; } @@ -1185,7 +1161,7 @@ class IDEInstInteractionAnalysisT private: /// Filters out all variables that had a non-empty set during edge functions /// computations. - inline std::unordered_set removeVariablesWithoutEmptySetValue( + std::unordered_set removeVariablesWithoutEmptySetValue( GenericSolverResults Solution, std::unordered_set Variables) const { // Check the solver results and remove all variables for which a diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h index 7407473e40..0ceb12f52c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h @@ -10,7 +10,9 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IDEProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" +#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -19,8 +21,6 @@ #include "llvm/Support/raw_ostream.h" #include -#include -#include #include namespace llvm { @@ -39,23 +39,15 @@ struct IDELinearConstantAnalysisDomain : public LLVMAnalysisDomainDefault { // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) class IDELinearConstantAnalysis - : public IDETabulationProblem { -public: - using IDETabProblemType = - IDETabulationProblem; - using typename IDETabProblemType::d_t; - using typename IDETabProblemType::f_t; - using typename IDETabProblemType::i_t; - using typename IDETabProblemType::l_t; - using typename IDETabProblemType::n_t; - using typename IDETabProblemType::t_t; - using typename IDETabProblemType::v_t; + : public IfdsIdeProblemMixin { + using base_t = IfdsIdeProblemMixin; +public: IDELinearConstantAnalysis(const LLVMProjectIRDB *IRDB, const LLVMBasedICFG *ICF, std::vector EntryPoints = {"main"}); - ~IDELinearConstantAnalysis() override; + ~IDELinearConstantAnalysis(); struct LCAResult { LCAResult() = default; @@ -64,7 +56,7 @@ class IDELinearConstantAnalysis std::map VariableToValue; std::vector IRTrace; void print(llvm::raw_ostream &OS) const; - inline bool operator==(const LCAResult &Rhs) const { + bool operator==(const LCAResult &Rhs) const { return SrcNode == Rhs.SrcNode && VariableToValue == Rhs.VariableToValue && IRTrace == Rhs.IRTrace; } @@ -76,45 +68,38 @@ class IDELinearConstantAnalysis // start formulating our analysis by specifying the parts required for IFDS - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ); - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override; + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun); FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitInst, n_t RetSite) override; - - FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override; - - FlowFunctionPtrType getSummaryFlowFunction(n_t Curr, f_t CalleeFun) override; + n_t ExitInst, n_t RetSite); - [[nodiscard]] InitialSeeds initialSeeds() override; + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees); - [[nodiscard]] d_t createZeroValue() const; + FlowFunctionPtrType getSummaryFlowFunction(n_t Curr, f_t CalleeFun); - [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; + [[nodiscard]] InitialSeeds initialSeeds(); // in addition provide specifications for the IDE parts EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t DestinationFunction, - d_t DestNode) override; + f_t DestinationFunction, d_t DestNode); EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, n_t ExitStmt, d_t ExitNode, - n_t RetSite, d_t RetNode) override; + n_t RetSite, d_t RetNode); - EdgeFunction - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, - d_t RetSiteNode, - llvm::ArrayRef Callees) override; + EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t RetSite, d_t RetSiteNode, + llvm::ArrayRef Callees); EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; + d_t SuccNode); // Helper functions @@ -122,7 +107,7 @@ class IDELinearConstantAnalysis getLCAResults(GenericSolverResults SR); void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override; + llvm::raw_ostream &OS = llvm::outs()); private: const LLVMBasedICFG *ICF{}; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index b01abd38fd..0731d9b10d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -13,7 +13,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -24,6 +24,7 @@ #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Printer.h" +#include "phasar/Utils/WithAnalysisPrinterMixin.h" #include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" @@ -62,9 +63,6 @@ class IDETypeStateAnalysisBase IDETypeStateAnalysisBase & operator=(const IDETypeStateAnalysisBase &) noexcept = delete; -protected: - IDETypeStateAnalysisBase(LLVMAliasInfoRef PT) noexcept : PT(PT) {} - using typename IDETypeStateAnalysisBaseCommon::container_type; using typename IDETypeStateAnalysisBaseCommon::d_t; using typename IDETypeStateAnalysisBaseCommon::f_t; @@ -81,6 +79,9 @@ class IDETypeStateAnalysisBase llvm::ArrayRef Callees); FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun); +protected: + IDETypeStateAnalysisBase(LLVMAliasInfoRef PT) noexcept : PT(PT) {} + // --- Utilities [[nodiscard]] virtual bool @@ -148,12 +149,14 @@ struct IDETypeStateAnalysisDomain : public LLVMAnalysisDomainDefault { template class IDETypeStateAnalysis - : public IDETabulationProblem< + : public IfdsIdeProblemMixin< IDETypeStateAnalysisDomain>, - private detail::IDETypeStateAnalysisBase { + public WithAnalysisPrinterMixin< + IDETypeStateAnalysisDomain>, + public detail::IDETypeStateAnalysisBase { public: using IDETabProblemType = - IDETabulationProblem>; + IfdsIdeProblemMixin>; using typename IDETabProblemType::container_type; using typename IDETabProblemType::d_t; using typename IDETabProblemType::f_t; @@ -354,51 +357,20 @@ class IDETypeStateAnalysis // start formulating our analysis by specifying the parts required for IFDS - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { - return detail::IDETypeStateAnalysisBase::getNormalFlowFunction(Curr, Succ); - } - - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override { - return detail::IDETypeStateAnalysisBase::getCallFlowFunction(CallSite, - DestFun); - } - - FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitStmt, n_t RetSite) override { - - return detail::IDETypeStateAnalysisBase::getRetFlowFunction( - CallSite, CalleeFun, ExitStmt, RetSite); - } - - FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override { - return detail::IDETypeStateAnalysisBase::getCallToRetFlowFunction( - CallSite, RetSite, Callees); - } - - FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, - f_t DestFun) override { - return detail::IDETypeStateAnalysisBase::getSummaryFlowFunction(CallSite, - DestFun); - } - - InitialSeeds initialSeeds() override { - return this->createDefaultSeeds(); + InitialSeeds initialSeeds() { + return IDETabProblemType::createDefaultSeeds(*this); } [[nodiscard]] d_t createZeroValue() const { return LLVMZeroValue::getInstance(); } - [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override { - return LLVMZeroValue::isLLVMZeroValue(Fact); - } + using detail::IDETypeStateAnalysisBase::getSummaryFlowFunction; // in addition provide specifications for the IDE parts EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t /*Succ*/, - d_t SuccNode) override { + d_t SuccNode) { // Set alloca instructions of target type to uninitialized. if (const auto *Alloca = llvm::dyn_cast(Curr)) { if (hasMatchingType(Alloca)) { @@ -412,22 +384,20 @@ class IDETypeStateAnalysis EdgeFunction getCallEdgeFunction(n_t /*CallSite*/, d_t /*SrcNode*/, f_t /*DestinationFunction*/, - d_t /*DestNode*/) override { + d_t /*DestNode*/) { return EdgeIdentity{}; } EdgeFunction getReturnEdgeFunction(n_t /*CallSite*/, f_t /*CalleeFunction*/, n_t /*ExitInst*/, d_t /*ExitNode*/, - n_t /*RetSite*/, - d_t /*RetNode*/) override { + n_t /*RetSite*/, d_t /*RetNode*/) { return EdgeIdentity{}; } - EdgeFunction - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t /*RetSite*/, - d_t RetSiteNode, - llvm::ArrayRef Callees) override { + EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, + n_t /*RetSite*/, d_t RetSiteNode, + llvm::ArrayRef Callees) { const auto *CS = llvm::cast(CallSite); for (const auto *Callee : Callees) { std::string DemangledFname = llvm::demangle(Callee->getName().str()); @@ -436,7 +406,7 @@ class IDETypeStateAnalysis // We apply the same edge function for the return value, i.e. callsite. if (TSD->isFactoryFunction(DemangledFname)) { PHASAR_LOG_LEVEL(DEBUG, "Processing factory function"); - if (isZeroValue(CallNode) && RetSiteNode == CS) { + if (this->isZeroValue(CallNode) && RetSiteNode == CS) { return TSConstant{ TSD->getNextState(DemangledFname, TSD->uninit(), CS), TSD}; } @@ -459,15 +429,9 @@ class IDETypeStateAnalysis return EdgeIdentity{}; } - EdgeFunction getSummaryEdgeFunction(n_t /*CallSite*/, d_t /*CallNode*/, - n_t /*RetSite*/, - d_t /*RetSiteNode*/) override { - return nullptr; - } - - l_t topElement() override { return TSD->top(); } + l_t topElement() { return TSD->top(); } - l_t bottomElement() override { return TSD->bottom(); } + l_t bottomElement() { return TSD->bottom(); } /** * We have a lattice with BOTTOM representing all information @@ -477,7 +441,7 @@ class IDETypeStateAnalysis * * @note Only one-level lattice's are handled currently */ - l_t join(l_t Lhs, l_t Rhs) override { + l_t join(l_t Lhs, l_t Rhs) { if (Lhs == Rhs) { return Lhs; } @@ -490,14 +454,6 @@ class IDETypeStateAnalysis return TSD->bottom(); } - EdgeFunction allTopFunction() override { - if constexpr (HasJoinLatticeTraits) { - return AllTop{}; - } else { - return AllTop{topElement()}; - } - } - [[nodiscard]] bool isAPIFunction(llvm::StringRef Name) const noexcept override { return TSD->isAPIFunction(Name); @@ -514,7 +470,7 @@ class IDETypeStateAnalysis } void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override { + llvm::raw_ostream &OS = llvm::outs()) { for (const auto &F : this->IRDB->getAllFunctions()) { for (const auto &BB : *F) { @@ -535,7 +491,7 @@ class IDETypeStateAnalysis } } - this->Printer->onFinalize(OS); + this->printer().onFinalize(OS); } [[nodiscard]] bool @@ -561,10 +517,6 @@ IDETypeStateAnalysis(const LLVMProjectIRDB *, LLVMAliasInfoRef, std::vector EntryPoints) -> IDETypeStateAnalysis; -// class CSTDFILEIOTypeStateDescription; - -// extern template class IDETypeStateAnalysis; - } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h index a6025c6254..d563ae06f7 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h @@ -10,12 +10,14 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H -#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IfdsIdeProblemMixin.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h" +#include "phasar/Utils/WithAnalysisPrinterMixin.h" #include #include @@ -36,11 +38,12 @@ class LLVMTaintConfig; * dedicated sink functions. A leak is reported once a tainted value * reached a sink function. * - * @see TaintConfiguration on how to specify your own + * @see LLVMTaintConfig on how to specify your own * taint-sensitive source and sink functions. */ class IFDSTaintAnalysis - : public IFDSTabulationProblem { + : public IfdsIdeProblemMixin, + public WithAnalysisPrinterMixin { struct KillsAtFn { const IFDSTaintAnalysis *Self{}; @@ -55,40 +58,30 @@ class IFDSTaintAnalysis /// Holds all leaks found during the analysis std::map> Leaks; - /** - * - * @param icfg - * @param TSF - * @param EntryPoints - */ IFDSTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, const LLVMTaintConfig *Config, std::vector EntryPoints = {"main"}, bool TaintMainArgs = true, bool EnableStrongUpdateStore = true); - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ); - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override; + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun); FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitStmt, n_t RetSite) override; + n_t ExitStmt, n_t RetSite); - FlowFunctionPtrType - getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override; + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees); - FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, - f_t DestFun) override; + FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun); - InitialSeeds initialSeeds() override; + InitialSeeds initialSeeds(); [[nodiscard]] d_t createZeroValue() const; - bool isZeroValue(d_t FlowFact) const noexcept override; - void emitTextReport(GenericSolverResults SR, - llvm::raw_ostream &OS = llvm::outs()) override; + llvm::raw_ostream &OS = llvm::outs()); [[nodiscard]] bool isInteresting(const llvm::Instruction *Inst) const noexcept; diff --git a/include/phasar/PhasarLLVM/DataFlow/MonoIfds/AliasCache.h b/include/phasar/PhasarLLVM/DataFlow/MonoIfds/AliasCache.h index 6ce02ccbe8..70fa8b49b3 100644 --- a/include/phasar/PhasarLLVM/DataFlow/MonoIfds/AliasCache.h +++ b/include/phasar/PhasarLLVM/DataFlow/MonoIfds/AliasCache.h @@ -11,6 +11,7 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/Utils/HashUtils.h" +#include "phasar/Utils/Macros.h" #include "phasar/Utils/UsedGlobalsHolder.h" #include "phasar/Utils/Utilities.h" @@ -33,12 +34,12 @@ class AliasCache { // Passed AI should already be FilteredAliasSet or similar explicit AliasCache( - LLVMAliasIteratorRef AI [[clang::lifetime_capture_by(this)]], + LLVMAliasIteratorRef AI PSR_LIFETIME_CAPTURE_BY(this), llvm::function_ref SkipSeedsCallBack - [[clang::lifetime_capture_by(this)]], + PSR_LIFETIME_CAPTURE_BY(this), const UsedGlobalsHolder::GlobalSet - *PermittedGlobals [[clang::lifetime_capture_by(this)]], - std::pmr::memory_resource *MRes [[clang::lifetime_capture_by(this)]]) + *PermittedGlobals PSR_LIFETIME_CAPTURE_BY(this), + std::pmr::memory_resource *MRes PSR_LIFETIME_CAPTURE_BY(this)) : AI(AI), SkipSeedsCallBack(SkipSeedsCallBack), PermittedGlobals(&assertNotNull(PermittedGlobals)), Cache(&assertNotNull(MRes)) {} diff --git a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h index 45bf95f948..6979fcced9 100644 --- a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h +++ b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_DOMAIN_LLVMANALYSISDOMAIN_H #include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Domain/BinaryDomain.h" #include "phasar/PhasarLLVM/Utils/LLVMAnalysisPrinter.h" #include "phasar/Utils/DefaultAnalysisPrinterSelector.h" #include "phasar/Utils/TypeTraits.h" @@ -56,6 +57,8 @@ struct LLVMAnalysisDomainDefault : public AnalysisDomain { using LLVMIFDSAnalysisDomainDefault = WithBinaryValueDomain; +static_assert(std::same_as); + extern template class DefaultLLVMAnalysisPrinter; template <> diff --git a/include/phasar/PhasarLLVM/TaintConfig.h b/include/phasar/PhasarLLVM/TaintConfig.h index 5335aaf590..8f10752ded 100644 --- a/include/phasar/PhasarLLVM/TaintConfig.h +++ b/include/phasar/PhasarLLVM/TaintConfig.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TAINTCONFIG_H #define PHASAR_PHASARLLVM_TAINTCONFIG_H +#include "phasar/PhasarLLVM/TaintConfig/DoubleFreeConfig.h" #include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" #include "phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h" #include "phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h" diff --git a/include/phasar/PhasarLLVM/TaintConfig/DoubleFreeConfig.h b/include/phasar/PhasarLLVM/TaintConfig/DoubleFreeConfig.h new file mode 100644 index 0000000000..1691796070 --- /dev/null +++ b/include/phasar/PhasarLLVM/TaintConfig/DoubleFreeConfig.h @@ -0,0 +1,16 @@ +#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/TaintConfig/LLVMTaintConfig.h" + +namespace psr { +LLVMTaintConfig getDoubleFreeConfig(); +} // namespace psr diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h index 2a4814f6dc..f929bd3888 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGBASE_H #include "phasar/PhasarLLVM/TaintConfig/TaintConfigData.h" +#include "phasar/Utils/Macros.h" #include "phasar/Utils/Nullable.h" #include "llvm/ADT/FunctionExtras.h" @@ -42,7 +43,7 @@ template class TaintConfigBase { llvm::unique_function(n_t) const>; using SkipSeedsCallBackTy = llvm::unique_function; - enum class [[clang::flag_enum]] SeedConfig { + enum class PSR_FLAG_ENUM SeedConfig { Arguments = 1, Instructions = 2, diff --git a/include/phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h index 3c3d808f81..b1845fd7eb 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMFunctionDataFlowFacts.h @@ -10,7 +10,7 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/DB/ProjectIRDB.h" #include "phasar/Utils/DefaultValue.h" #include "phasar/Utils/FunctionDataFlowFacts.h" #include "phasar/Utils/MapUtils.h" @@ -23,10 +23,14 @@ namespace psr::library_summary { class LLVMFunctionDataFlowFacts; + [[nodiscard]] LLVMFunctionDataFlowFacts readFromFDFF(const FunctionDataFlowFacts &Fdff, std::invocable auto GetFunctionByNameOrNull); +[[nodiscard]] LLVMFunctionDataFlowFacts +readFromFDFF(const FunctionDataFlowFacts &Fdff, const ProjectIRDB auto &IRDB); + /// @brief A LLVM-specific mapping of FunctionDataFlowFacts class LLVMFunctionDataFlowFacts { public: @@ -78,6 +82,13 @@ class LLVMFunctionDataFlowFacts { return Ret; } + [[nodiscard]] friend LLVMFunctionDataFlowFacts + readFromFDFF(const FunctionDataFlowFacts &Fdff, + const ProjectIRDB auto &IRDB) { + return readFromFDFF( + Fdff, [&IRDB](llvm::StringRef Name) { return IRDB.getFunction(Name); }); + } + private: llvm::DenseMap LLVMFdff; }; diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index f57aea4b11..91b65eab62 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -58,7 +58,7 @@ struct AliasInfoTraits> : DefaultAATraits {}; /// NOTE: AliasInfoRef::mergeWith() only works if supplied with a compatible /// other AliasInfo. Otherwise, it asserts out template -class [[gsl::Pointer]] AliasInfoRef +class PSR_POINTER() AliasInfoRef : public AnalysisPropertiesMixin> { friend class AliasInfo; @@ -306,8 +306,7 @@ class [[gsl::Pointer]] AliasInfoRef /// \endcode /// template -class [[clang::trivial_abi, gsl::Owner]] AliasInfo final - : public AliasInfoRef { +class PSR_TRIVIAL_ABI PSR_OWNER() AliasInfo final : public AliasInfoRef { using base_t = AliasInfoRef; public: diff --git a/include/phasar/Pointer/AliasIterator.h b/include/phasar/Pointer/AliasIterator.h index 94c549b26b..ef2cd330af 100644 --- a/include/phasar/Pointer/AliasIterator.h +++ b/include/phasar/Pointer/AliasIterator.h @@ -65,7 +65,7 @@ concept IsAliasIterator = requires { /// LLVMAliasIteratorRef AA = &ASet; /// \endcode template -class [[gsl::Pointer]] AliasIteratorRef : private TypeErasureUtils { +class PSR_POINTER() AliasIteratorRef : private TypeErasureUtils { public: using n_t = N; using v_t = V; diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index a7edf589c5..81d12a030b 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -217,7 +217,7 @@ class PointsToInfoRef /// Implicitly convertible to PointsToInfoRef. /// template -class [[clang::trivial_abi]] PointsToInfo final +class PSR_TRIVIAL_ABI PointsToInfo final : public PointsToInfoRef { using base_t = PointsToInfoRef; diff --git a/include/phasar/Pointer/PointsToIterator.h b/include/phasar/Pointer/PointsToIterator.h index 18f19aa2ba..1fe2345098 100644 --- a/include/phasar/Pointer/PointsToIterator.h +++ b/include/phasar/Pointer/PointsToIterator.h @@ -75,7 +75,7 @@ concept IsPointsToIterator = /// \note You can also convert an AliasInfoRef to PointsToIteratorRef. In this /// case, it will use the getReachableAllocationSites() API. template -class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { +class PSR_POINTER() PointsToIteratorRef : protected TypeErasureUtils { public: using v_t = V; using o_t = O; @@ -272,7 +272,7 @@ class [[gsl::Pointer]] PointsToIteratorRef : protected TypeErasureUtils { /// Owning version of PointsToIteratorRef template -class [[clang::trivial_abi, gsl::Owner]] PointsToIterator +class PSR_TRIVIAL_ABI PSR_OWNER() PointsToIterator : public PointsToIteratorRef { using base_t = PointsToIteratorRef; diff --git a/include/phasar/Pointer/UnionFindAA.h b/include/phasar/Pointer/UnionFindAA.h index f48472fa67..67f2564f47 100644 --- a/include/phasar/Pointer/UnionFindAA.h +++ b/include/phasar/Pointer/UnionFindAA.h @@ -194,9 +194,7 @@ struct BasicUnionFindAAResult : UnionFindAAResultBase { TypedVector Var2Rep; [[nodiscard]] static constexpr bool isCached() noexcept { return true; } - [[nodiscard]] constexpr size_t size() const noexcept { - return Var2Rep.size(); - } + [[nodiscard]] size_t size() const noexcept { return Var2Rep.size(); } [[nodiscard]] const RawAliasSet & getRawAliasSet(ValueId Var) const noexcept { @@ -230,9 +228,7 @@ struct CallingContextSensUnionFindAAResult : UnionFindAAResultBase { TypedVector> Var2Rep{}; [[nodiscard]] static constexpr bool isCached() noexcept { return false; } - [[nodiscard]] constexpr size_t size() const noexcept { - return Var2Rep.size(); - } + [[nodiscard]] size_t size() const noexcept { return Var2Rep.size(); } [[nodiscard]] RawAliasSet getRawAliasSet(ValueId Var) const { RawAliasSet ResultSet; diff --git a/include/phasar/Pointer/UnionFindAliasAnalysisType.h b/include/phasar/Pointer/UnionFindAliasAnalysisType.h index dec47142fa..2885a0bf0f 100644 --- a/include/phasar/Pointer/UnionFindAliasAnalysisType.h +++ b/include/phasar/Pointer/UnionFindAliasAnalysisType.h @@ -10,6 +10,7 @@ *****************************************************************************/ #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" namespace psr { enum class UnionFindAliasAnalysisType : uint8_t { @@ -25,5 +26,7 @@ to_string(UnionFindAliasAnalysisType AType) noexcept { return CMDFLAG; #include "phasar/Pointer/UnionFindAAType.def" } + + llvm_unreachable("All union-find AA types handled in the switch above"); } } // namespace psr diff --git a/include/phasar/Utils/JoinLattice.h b/include/phasar/Utils/JoinLattice.h index e2f120fee2..7bd6694ef3 100644 --- a/include/phasar/Utils/JoinLattice.h +++ b/include/phasar/Utils/JoinLattice.h @@ -76,6 +76,27 @@ class JoinLattice { }; }; +template class DerivedJoinLattice { +public: + using l_t = L; + + [[nodiscard]] constexpr l_t topElement() + requires HasJoinLatticeTraits + { + return JoinLatticeTraits::top(); + } + [[nodiscard]] constexpr l_t bottomElement() + requires HasJoinLatticeTraits + { + return JoinLatticeTraits::bottom(); + } + [[nodiscard]] constexpr l_t join(l_t Lhs, l_t Rhs) + requires HasJoinLatticeTraits + { + return JoinLatticeTraits::join(std::move(Lhs), std::move(Rhs)); + } +}; + template struct NonTopBotValue { using type = L; diff --git a/include/phasar/Utils/Macros.h b/include/phasar/Utils/Macros.h index 74d3c6690f..f51dd32e81 100644 --- a/include/phasar/Utils/Macros.h +++ b/include/phasar/Utils/Macros.h @@ -29,10 +29,65 @@ #if __has_cpp_attribute(clang::lifetimebound) #define PSR_LIFETIMEBOUND [[clang::lifetimebound]] -#elif __has_cpp_attribute([[lifetimebound]]) +#elif __has_cpp_attribute(lifetimebound) #define PSR_LIFETIMEBOUND [[lifetimebound]] #else #define PSR_LIFETIMEBOUND #endif +#if __has_cpp_attribute(clang::lifetime_capture_by) +#define PSR_LIFETIME_CAPTURE_BY(...) [[clang::lifetime_capture_by(__VA_ARGS__)]] +#else +#define PSR_LIFETIME_CAPTURE_BY(...) +#endif + +#if __has_cpp_attribute(clang::internal_linkage) +#define PSR_INTERNAL_LINKAGE [[clang::internal_linkage]] +#else +#define PSR_INTERNAL_LINKAGE +#endif + +#if __has_cpp_attribute(clang::trivial_abi) +#define PSR_TRIVIAL_ABI [[clang::trivial_abi]] +#else +#define PSR_TRIVIAL_ABI +#endif + +#if __has_cpp_attribute(clang::require_explicit_initialization) +#define PSR_REQUIRE_EXPLICIT_INITIALIZATION \ + [[clang::require_explicit_initialization]] +#else +#define PSR_REQUIRE_EXPLICIT_INITIALIZATION +#endif + +#if __has_cpp_attribute(clang::enum_extensibility) +#define PSR_ENUM_EXTENSIBILITY(...) [[clang::enum_extensibility(__VA_ARGS__)]] +#else +#define PSR_ENUM_EXTENSIBILITY(...) +#endif + +#if __has_cpp_attribute(gsl::Owner) +#define PSR_OWNER(...) [[gsl::Owner(__VA_ARGS__)]] +#else +#define PSR_OWNER(...) +#endif + +#if __has_cpp_attribute(gsl::Pointer) +#define PSR_POINTER(...) [[gsl::Pointer(__VA_ARGS__)]] +#else +#define PSR_POINTER(...) +#endif + +#if __has_cpp_attribute(clang::flag_enum) +#define PSR_FLAG_ENUM [[clang::flag_enum]] +#else +#define PSR_FLAG_ENUM +#endif + +#if __has_cpp_attribute(clang::preferred_name) +#define PSR_PREFERRED_NAME(...) [[clang::preferred_name(__VA_ARGS__)]] +#else +#define PSR_PREFERRED_NAME(...) +#endif + #endif // PHASAR_UTILS_MACROS_H diff --git a/include/phasar/Utils/MaybeUniquePtr.h b/include/phasar/Utils/MaybeUniquePtr.h index 7ad6d12155..a9a6e9b6b1 100644 --- a/include/phasar/Utils/MaybeUniquePtr.h +++ b/include/phasar/Utils/MaybeUniquePtr.h @@ -10,6 +10,8 @@ #ifndef PHASAR_UTILS_MAYBEUNIQUEPTR_H_ #define PHASAR_UTILS_MAYBEUNIQUEPTR_H_ +#include "phasar/Utils/Macros.h" + #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/PointerLikeTypeTraits.h" @@ -59,7 +61,7 @@ template class MaybeUniquePtrBase { /// \tparam RequireAlignment If true, the datastructure only works if /// alignof(T) > 1 holds. Enables incomplete T types template -class [[clang::trivial_abi]] MaybeUniquePtr +class PSR_TRIVIAL_ABI MaybeUniquePtr : detail::MaybeUniquePtrBase { template friend class MaybeUniquePtr; diff --git a/include/phasar/Utils/NonNullPtr.h b/include/phasar/Utils/NonNullPtr.h index 0a978a5ede..f4cdded440 100644 --- a/include/phasar/Utils/NonNullPtr.h +++ b/include/phasar/Utils/NonNullPtr.h @@ -17,7 +17,7 @@ namespace psr { template -class [[gsl::Pointer(T)]] NonNullPtr : public std::reference_wrapper { +class PSR_POINTER(T) NonNullPtr : public std::reference_wrapper { using Base = std::reference_wrapper; public: diff --git a/include/phasar/Utils/Nullable.h b/include/phasar/Utils/Nullable.h index 8a96f062a0..4d23f041e4 100644 --- a/include/phasar/Utils/Nullable.h +++ b/include/phasar/Utils/Nullable.h @@ -18,30 +18,34 @@ namespace psr { template -using Nullable = - std::conditional_t, T, std::optional>; +concept IsNullable = + requires(T Val) { bool(Val); } && !std::is_arithmetic_v && + !std::is_enum_v && !std::is_member_pointer_v; template - requires std::is_convertible_v +using Nullable = std::conditional_t, T, std::optional>; + +template + requires IsNullable [[nodiscard]] constexpr T unwrapNullable(T &&Val) noexcept { assert(Val && "Unwrapping null-value!"); return std::forward(Val); } template - requires(!std::is_convertible_v) + requires(!IsNullable) [[nodiscard]] constexpr T unwrapNullable(std::optional &&Val) noexcept { assert(Val && "Unwrapping nullopt!"); return *std::move(Val); } template - requires(!std::is_convertible_v) + requires(!IsNullable) [[nodiscard]] constexpr const T & unwrapNullable(const std::optional &Val) noexcept { assert(Val && "Unwrapping nullopt!"); return *Val; } template - requires(!std::is_convertible_v) + requires(!IsNullable) [[nodiscard]] constexpr T &unwrapNullable(std::optional &Val) noexcept { assert(Val && "Unwrapping nullopt!"); return *Val; diff --git a/include/phasar/Utils/PointerUtils.h b/include/phasar/Utils/PointerUtils.h index 04cd723b29..23de61b4c7 100644 --- a/include/phasar/Utils/PointerUtils.h +++ b/include/phasar/Utils/PointerUtils.h @@ -3,6 +3,7 @@ #include "phasar/Utils/BoxedPointer.h" #include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/NonNullPtr.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" @@ -93,6 +94,12 @@ getPointerFrom(BoxedConstPtr Ptr) noexcept { return Ptr; } +template +[[nodiscard]] LLVM_ATTRIBUTE_RETURNS_NONNULL constexpr T * +getPointerFrom(NonNullPtr Ptr) noexcept { + return Ptr.get(); +} + static_assert( std::is_same_v &>()))>); diff --git a/include/phasar/Utils/SemiRing.h b/include/phasar/Utils/SemiRing.h index 0976e1d809..332d6a9c6b 100644 --- a/include/phasar/Utils/SemiRing.h +++ b/include/phasar/Utils/SemiRing.h @@ -12,26 +12,32 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/JoinLattice.h" #include namespace psr { -template -concept IsSemiRing = requires(T &SR, const typename T::EdgeFunctionType &EF) { - typename T::EdgeFunctionType; - requires IsEdgeFunction; - - { SR.extend(EF, EF) } -> std::convertible_to; - { SR.combine(EF, EF) } -> std::convertible_to; - { SR.identity() } -> std::convertible_to; +template class AllTopFnProvider { +public: + virtual ~AllTopFnProvider() = default; + /// Returns an edge function that represents the top element of the analysis. + virtual EdgeFunction allTopFunction() = 0; }; -template -concept IsSemiRingOf = IsSemiRing && requires { - requires std::same_as; + +template + requires HasJoinLatticeTraits +class AllTopFnProvider { +public: + virtual ~AllTopFnProvider() = default; + /// Returns an edge function that represents the top element of the analysis. + virtual EdgeFunction allTopFunction() { + return AllTop{}; + } }; -template class SemiRing { +template +class SemiRing : public AllTopFnProvider { public: using l_t = typename AnalysisDomainTy::l_t; using EdgeFunctionType = EdgeFunction; @@ -48,11 +54,31 @@ template class SemiRing { return L.joinWith(R); } - virtual EdgeFunctionType identity() const { return EdgeIdentity{}; } + virtual EdgeFunction identity() { return EdgeIdentity{}; } + + using AllTopFnProvider::allTopFunction; +}; + +template +concept IsSemiRing = requires(T &SR, const typename T::EdgeFunctionType &CEF) { + typename T::EdgeFunctionType; + requires IsEdgeFunction; + + { SR.extend(CEF, CEF) } -> std::convertible_to; + + { SR.combine(CEF, CEF) } -> std::convertible_to; + + { SR.identity() } -> std::convertible_to; +}; + +template +concept HasAllTopFunction = requires(T &SR) { + { SR.allTopFunction() } -> std::convertible_to; }; struct BinarySemiRing { - using EdgeFunctionType = EdgeIdentity; + using l_t = BinaryDomain; + using EdgeFunctionType = EdgeIdentity; [[nodiscard]] constexpr EdgeFunctionType extend(EdgeFunctionType /*L*/, EdgeFunctionType /*R*/) const noexcept { @@ -72,4 +98,30 @@ struct BinarySemiRing { }; inline constinit BinarySemiRing BinarySemiRing::Instance{}; + +template struct DefaultSemiRing { + using l_t = L; + using EdgeFunctionType = EdgeFunction; + + [[nodiscard]] constexpr EdgeFunction + extend(const EdgeFunction &Lhs, const EdgeFunction &Rhs) { + return Lhs.composeWith(Rhs); + } + + [[nodiscard]] constexpr EdgeFunction + combine(const EdgeFunction &Lhs, const EdgeFunction &Rhs) { + return Lhs.joinWith(Rhs); + } + + [[nodiscard]] constexpr auto identity() { return EdgeIdentity{}; } + + [[nodiscard]] constexpr auto allTopFunction() + requires HasJoinLatticeTraits + { + return AllTop{}; + } +}; + +template <> struct DefaultSemiRing : public BinarySemiRing {}; + } // namespace psr diff --git a/include/phasar/Utils/StrongTypeDef.h b/include/phasar/Utils/StrongTypeDef.h index 063681e204..94a3fe1bf5 100644 --- a/include/phasar/Utils/StrongTypeDef.h +++ b/include/phasar/Utils/StrongTypeDef.h @@ -10,6 +10,7 @@ *****************************************************************************/ #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Macros.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/Support/HashBuilder.h" @@ -20,7 +21,7 @@ #define PHASAR_STRONG_TYPEDEF(NAMESPACE, TYPE, NAME, ...) \ namespace NAMESPACE { \ - enum class [[clang::enum_extensibility(open)]] NAME : TYPE { __VA_ARGS__ }; \ + enum class PSR_ENUM_EXTENSIBILITY(open) NAME : TYPE { __VA_ARGS__ }; \ } \ namespace llvm { \ template <> struct DenseMapInfo { \ diff --git a/include/phasar/Utils/Table.h b/include/phasar/Utils/Table.h index 77734bfde4..024da0c033 100644 --- a/include/phasar/Utils/Table.h +++ b/include/phasar/Utils/Table.h @@ -46,7 +46,8 @@ template class Table { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Cell &Cell) { - return OS << "Cell: " << Cell.r << ", " << Cell.c << ", " << Cell.v; + return OS << "Cell: " << Cell.Row << ", " << Cell.Column << ", " + << Cell.Value; } friend bool operator<(const Cell &Lhs, const Cell &Rhs) noexcept { return std::tie(Lhs.Row, Lhs.Column, Lhs.Value) < diff --git a/include/phasar/Utils/WithAnalysisPrinterMixin.h b/include/phasar/Utils/WithAnalysisPrinterMixin.h new file mode 100644 index 0000000000..2f0a24bdaa --- /dev/null +++ b/include/phasar/Utils/WithAnalysisPrinterMixin.h @@ -0,0 +1,80 @@ +#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/DataFlow/IfdsIde/Solver/GenericSolverResults.h" +#include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/DefaultAnalysisPrinterSelector.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/NullAnalysisPrinter.h" + +namespace psr { +template class WithAnalysisPrinterMixin { + // NOTE: Don't add type-aliases for n_t,d_t, ... here; this will break the + // compilation with gcc; gcc apparently ignores private visibility when + // checking for ambiguous declarations. clang instead behaves as expected. + +public: + WithAnalysisPrinterMixin() + : Printer(std::make_unique::type>()) {} + + WithAnalysisPrinterMixin( + MaybeUniquePtr> Printer) noexcept + : Printer(std::move(Printer)) {} + + constexpr auto setAnalysisPrinter( + MaybeUniquePtr> P) noexcept { + if (P) { + return std::exchange(Printer, std::move(P)); + } + + return std::exchange(Printer, + NullAnalysisPrinter::getInstance()); + } + + [[nodiscard]] constexpr auto consumePrinter() noexcept { + return setAnalysisPrinter(nullptr); + } + + [[nodiscard]] constexpr AnalysisPrinterBase & + printer() noexcept { + assert(Printer != nullptr); + return *Printer; + } + + void emitTextReport( + [[maybe_unused]] GenericSolverResultsFor Results, + llvm::raw_ostream &OS = llvm::outs()) { + Printer->onFinalize(OS); + } + +protected: + template ::l_t> + void onResult(AnalysisDomainTy::n_t Instr, D &&DfFact, L &&LatticeElement, + DataFlowAnalysisType AnalysisType) { + Printer->onResult(Instr, PSR_FWD(DfFact), PSR_FWD(LatticeElement), + AnalysisType); + } + + template + requires std::is_same_v< + typename detail::ValueDomainAdder::l_t, BinaryDomain> + void onResult(AnalysisDomainTy::n_t Instr, D &&DfFact, + DataFlowAnalysisType AnalysisType) { + Printer->onResult(Instr, PSR_FWD(DfFact), AnalysisType); + } + +private: + MaybeUniquePtr> Printer; +}; +} // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp index f02934c5db..04165506e0 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/CFLFieldSensIFDSProblem.cpp @@ -379,11 +379,10 @@ llvm::raw_ostream &psr::cfl_fieldsens::operator<<(llvm::raw_ostream &OS, } InitialSeeds -cfl_fieldsens::makeInitialSeeds( +CFLFieldSensEdgeFunctions::makeInitialSeeds( const InitialSeeds - &UserSeeds, - FieldStringManager &Mgr) { + &UserSeeds) { InitialSeeds::GeneralizedSeeds Ret; @@ -402,21 +401,21 @@ llvm::raw_ostream &cfl_fieldsens::operator<<(llvm::raw_ostream &OS, return OS << "Txn[" << EF.Impl->Transform << ']'; } -EdgeFunction CFLFieldSensIFDSProblem::makeEF( +EdgeFunction CFLFieldSensEdgeFunctions::makeEF( cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF) { auto It = EFInternCache.insert(std::move(EF)); return CFLFieldSensEdgeFunction{&*It.first}; } -auto CFLFieldSensIFDSProblem::makeEFPtr( +auto CFLFieldSensEdgeFunctions::makeEFPtr( cfl_fieldsens::CFLFieldSensEdgeFunctionImpl &&EF) -> EFResultPtr { auto It = EFInternCache.insert(std::move(EF)); return EFResultPtr{&*It.first}; } -auto CFLFieldSensIFDSProblem::getStoreEdgeFunction(d_t CurrNode, d_t SuccNode, - d_t PointerOp, d_t ValueOp, - uint8_t DepthKLimit, - const llvm::DataLayout &DL) +auto CFLFieldSensEdgeFunctions::getStoreEdgeFunction(d_t CurrNode, d_t SuccNode, + d_t PointerOp, d_t ValueOp, + uint8_t DepthKLimit, + const llvm::DataLayout &DL) -> EdgeFunction { auto [BasePtr, Offset] = getBaseAndOffset(PointerOp, DL); @@ -461,9 +460,9 @@ auto CFLFieldSensIFDSProblem::getStoreEdgeFunction(d_t CurrNode, d_t SuccNode, return EdgeIdentity{}; } -auto CFLFieldSensIFDSProblem::getLoadEdgeFunction(d_t CurrNode, d_t PointerOp, - uint8_t DepthKLimit, - const llvm::DataLayout &DL) +auto CFLFieldSensEdgeFunctions::getLoadEdgeFunction(d_t CurrNode, d_t PointerOp, + uint8_t DepthKLimit, + const llvm::DataLayout &DL) -> EdgeFunction { const auto *ZeroOffsBase = PointerOp->stripPointerCastsAndAliases(); @@ -488,8 +487,9 @@ auto CFLFieldSensIFDSProblem::getLoadEdgeFunction(d_t CurrNode, d_t PointerOp, CFLFieldSensEdgeFunctionImpl::from(FieldString, Mgr, DepthKLimit)); } -auto CFLFieldSensIFDSProblem::getNormalEdgeFunction(n_t Curr, d_t CurrNode, - n_t /*Succ*/, d_t SuccNode) +auto CFLFieldSensEdgeFunctions::getNormalEdgeFunction(n_t Curr, d_t CurrNode, + n_t /*Succ*/, + d_t SuccNode) -> EdgeFunction { PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, "[getNormalEdgeFunction]:"); PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, " Curr: " << NToString(Curr)); @@ -532,9 +532,9 @@ auto CFLFieldSensIFDSProblem::getNormalEdgeFunction(n_t Curr, d_t CurrNode, return EdgeIdentity{}; } -auto CFLFieldSensIFDSProblem::getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t /*DestinationFunction*/, - d_t DestNode) +auto CFLFieldSensEdgeFunctions::getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t /*DestinationFunction*/, + d_t DestNode) -> EdgeFunction { PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, "[getCallEdgeFunction]"); PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, " Curr: " << NToString(CallSite)); @@ -553,7 +553,7 @@ auto CFLFieldSensIFDSProblem::getCallEdgeFunction(n_t CallSite, d_t SrcNode, return EdgeIdentity{}; } -auto CFLFieldSensIFDSProblem::getReturnEdgeFunction( +auto CFLFieldSensEdgeFunctions::getReturnEdgeFunction( n_t /*CallSite*/, f_t /*CalleeFunction*/, n_t ExitStmt, d_t ExitNode, n_t /*RetSite*/, d_t RetNode) -> EdgeFunction { PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, "[getReturnEdgeFunction]"); @@ -572,7 +572,7 @@ auto CFLFieldSensIFDSProblem::getReturnEdgeFunction( return EdgeIdentity{}; } -auto CFLFieldSensIFDSProblem::getCallToRetEdgeFunction( +auto CFLFieldSensEdgeFunctions::getCallToRetEdgeFunction( n_t CallSite, d_t CallNode, n_t /*RetSite*/, d_t RetSiteNode, llvm::ArrayRef /*Callees*/) -> EdgeFunction { @@ -603,8 +603,9 @@ auto CFLFieldSensIFDSProblem::getCallToRetEdgeFunction( return EdgeIdentity{}; } -auto CFLFieldSensIFDSProblem::getSummaryEdgeFunction(n_t Curr, d_t CurrNode, - n_t /*Succ*/, d_t SuccNode) +auto CFLFieldSensEdgeFunctions::getSummaryEdgeFunction(n_t Curr, d_t CurrNode, + n_t /*Succ*/, + d_t SuccNode) -> EdgeFunction { PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, "[getSummaryEdgeFunction]"); @@ -770,7 +771,7 @@ getResultEF(llvm::PointerIntPair } } -void CFLFieldSensIFDSProblem::regCounters() noexcept { +void CFLFieldSensEdgeFunctions::regCounters() noexcept { PAMM_GET_INSTANCE; REG_COUNTER("ExtendCache Refs", 0, Full); @@ -789,8 +790,8 @@ void CFLFieldSensIFDSProblem::regCounters() noexcept { REG_COUNTER("getResultEF Ptr", 0, Full); } -auto CFLFieldSensIFDSProblem::extend(const EdgeFunction &L, - const EdgeFunction &R) +auto CFLFieldSensEdgeFunctions::extend(const EdgeFunction &L, + const EdgeFunction &R) -> EdgeFunction { auto Ret = [&]() -> EdgeFunction { if (auto DfltCompose = psr::defaultComposeOrNull(L, R)) { @@ -848,8 +849,8 @@ auto CFLFieldSensIFDSProblem::extend(const EdgeFunction &L, return Ret; } -auto CFLFieldSensIFDSProblem::combine(const EdgeFunction &L, - const EdgeFunction &R) +auto CFLFieldSensEdgeFunctions::combine(const EdgeFunction &L, + const EdgeFunction &R) -> EdgeFunction { if (auto Dflt = defaultJoinOrNullNoId(L, R)) { return Dflt; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/IFDSIDESolverConfig.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/IFDSIDESolverConfig.cpp index 7e6d854ad9..7d828dd63b 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/IFDSIDESolverConfig.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/IFDSIDESolverConfig.cpp @@ -9,66 +9,20 @@ #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" -#include +#include "llvm/Support/raw_ostream.h" -using namespace std; using namespace psr; -namespace psr { - -IFDSIDESolverConfig::IFDSIDESolverConfig(SolverConfigOptions Options) noexcept - : Options(Options) {} - -bool IFDSIDESolverConfig::followReturnsPastSeeds() const { - return hasFlag(Options, SolverConfigOptions::FollowReturnsPastSeeds); -} -bool IFDSIDESolverConfig::autoAddZero() const { - return hasFlag(Options, SolverConfigOptions::AutoAddZero); -} -bool IFDSIDESolverConfig::computeValues() const { - return hasFlag(Options, SolverConfigOptions::ComputeValues); -} -bool IFDSIDESolverConfig::recordEdges() const { - return hasFlag(Options, SolverConfigOptions::RecordEdges); -} -bool IFDSIDESolverConfig::emitESG() const { - return hasFlag(Options, SolverConfigOptions::EmitESG); -} -bool IFDSIDESolverConfig::computePersistedSummaries() const { - return hasFlag(Options, SolverConfigOptions::ComputePersistedSummaries); -} - -void IFDSIDESolverConfig::setFollowReturnsPastSeeds(bool Set) { - setFlag(Options, SolverConfigOptions::FollowReturnsPastSeeds, Set); -} -void IFDSIDESolverConfig::setAutoAddZero(bool Set) { - setFlag(Options, SolverConfigOptions::AutoAddZero, Set); -} -void IFDSIDESolverConfig::setComputeValues(bool Set) { - setFlag(Options, SolverConfigOptions::ComputeValues, Set); -} -void IFDSIDESolverConfig::setRecordEdges(bool Set) { - setFlag(Options, SolverConfigOptions::RecordEdges, Set); -} -void IFDSIDESolverConfig::setEmitESG(bool Set) { - setFlag(Options, SolverConfigOptions::EmitESG, Set); -} -void IFDSIDESolverConfig::setComputePersistedSummaries(bool Set) { - setFlag(Options, SolverConfigOptions::ComputePersistedSummaries, Set); -} - -void IFDSIDESolverConfig::setConfig(SolverConfigOptions Opt) { Options = Opt; } - -ostream &operator<<(ostream &OS, const IFDSIDESolverConfig &SC) { +llvm::raw_ostream &psr::operator<<(llvm::raw_ostream &OS, + IFDSIDESolverConfig SC) { + const auto BoolAlpha = [](bool B) { return B ? "true" : "false"; }; return OS << "IFDSIDESolverConfig:\n" - << "\tfollowReturnsPastSeeds: " << SC.followReturnsPastSeeds() - << "\n" - << "\tautoAddZero: " << std::boolalpha << SC.autoAddZero() << "\n" - << "\tcomputeValues: " << SC.computeValues() << "\n" - << "\trecordEdges: " << SC.recordEdges() << "\n" - << "\tcomputePersistedSummaries: " << SC.computePersistedSummaries() - << "\n" - << "\temitESG: " << SC.emitESG(); + << "\tfollowReturnsPastSeeds: " + << BoolAlpha(SC.followReturnsPastSeeds()) << '\n' + << "\tautoAddZero: " << BoolAlpha(SC.autoAddZero()) << '\n' + << "\tcomputeValues: " << BoolAlpha(SC.computeValues()) << '\n' + << "\trecordEdges: " << BoolAlpha(SC.recordEdges()) << '\n' + << "\tcomputePersistedSummaries: " + << BoolAlpha(SC.computePersistedSummaries()) << '\n' + << "\temitESG: " << BoolAlpha(SC.emitESG()); } - -} // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.cpp index 1528a81dff..a582b2b6ba 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.cpp @@ -6,7 +6,8 @@ #include "llvm/IR/Instructions.h" namespace psr::XTaint { -AnalysisBase::AnalysisBase(const LLVMTaintConfig *TSF) noexcept : TSF(TSF) { +AnalysisBase::AnalysisBase(const LLVMTaintConfig *TSF, size_t NumInstructions) + : TSF(TSF), FactFactory(NumInstructions) { assert(TSF != nullptr); } diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp index 83e0d91788..4617d48735 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.cpp @@ -10,8 +10,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" -#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" -#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/EntryPointUtils.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h" @@ -20,20 +19,17 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Pointer/PointsToInfo.h" #include "phasar/Utils/DebugOutput.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Printer.h" #include "phasar/Utils/Utilities.h" -#include "llvm/ADT/SmallSet.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/Casting.h" #include "llvm/Support/WithColor.h" #include -#include namespace psr::XTaint { @@ -51,8 +47,8 @@ IDEExtendedTaintAnalysis::initialSeeds() { } } - addSeedsForStartingPoints(base_t::EntryPoints, ICF, Seeds, - this->base_t::getZeroValue(), bottomElement()); + addSeedsForStartingPoints(this->EntryPoints, ICF, Seeds, this->getZeroValue(), + bottomElement()); if (Seeds.empty()) { llvm::WithColor::warning() @@ -64,10 +60,6 @@ IDEExtendedTaintAnalysis::initialSeeds() { return Seeds; } -auto IDEExtendedTaintAnalysis::createZeroValue() const -> d_t { - return FactFactory.getOrCreateZero(); -} - bool IDEExtendedTaintAnalysis::isZeroValue(d_t Fact) const noexcept { return Fact->isZero(); } @@ -216,7 +208,7 @@ void IDEExtendedTaintAnalysis::generateFromZero(std::set &Dest, bool IncludeActualArg) { if (const auto &SourceCB = TSF->getRegisteredSourceCallBack(); TSF->isSource(ActualArg) || - (SourceCB && SourceCB(Inst).count(ActualArg))) { + (SourceCB && SourceCB(Inst).contains(ActualArg))) { Dest.insert(makeFlowFact(FormalArg)); if (IncludeActualArg) { Dest.insert(makeFlowFact(ActualArg)); @@ -229,8 +221,8 @@ void IDEExtendedTaintAnalysis::reportLeakIfNecessary( const llvm::Value *LeakCandidate) { if (isSink(SinkCandidate, Inst)) { Leaks[Inst].insert(LeakCandidate); - Printer->onResult(Inst, makeFlowFact(LeakCandidate), Top{}, - DataFlowAnalysisType::IDEExtendedTaintAnalysis); + onResult(Inst, makeFlowFact(LeakCandidate), Top{}, + DataFlowAnalysisType::IDEExtendedTaintAnalysis); } } @@ -738,7 +730,7 @@ void IDEExtendedTaintAnalysis::emitTextReport( } } - Printer->onFinalize(OS); + printer().onFinalize(OS); } // Helpers: diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp index ac7d560020..94f6bfb2d1 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp @@ -34,7 +34,7 @@ using d_t = IDEFeatureTaintAnalysisDomain::d_t; IDEFeatureTaintAnalysis::IDEFeatureTaintAnalysis( const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, std::vector EntryPoints, FeatureTaintGenerator &&TaintGen) - : IDETabulationProblem( + : IfdsIdeProblemMixin( IRDB, std::move(EntryPoints), LLVMZeroValue::getInstance()), TaintGen(std::move(TaintGen)), PT(PT) {} @@ -93,7 +93,7 @@ static bool canKillPointerOp(const llvm::Value *PointerOp, if (llvm::isa(Src) || llvm::isa(PointerOp)) { - return PointerOpMayAliases.count(Src); + return PointerOpMayAliases.contains(Src); } return false; @@ -115,7 +115,7 @@ static auto getStoreFF(bool GeneratesFact, LLVMAliasInfoRef PT, [Dest, Value, PointerRet = std::move(PointerRet), ValuePTS = std::move(ValuePTS), GeneratesFact](d_t Src) -> container_type { - if (Dest == Src || (PointerRet.count(Src) && + if (Dest == Src || (PointerRet.contains(Src) && canKillPointerOp(Dest, Src, PointerRet))) { return {}; } @@ -126,7 +126,7 @@ static auto getStoreFF(bool GeneratesFact, LLVMAliasInfoRef PT, // ... or from zero, if we manually generate a fact here if (Value == Src || (GeneratesFact && LLVMZeroValue::isLLVMZeroValue(Src)) || - ValuePTS->count(Src)) { + ValuePTS->contains(Src)) { return PointerRet; } @@ -157,7 +157,7 @@ auto IDEFeatureTaintAnalysis::getNormalFlowFunction(n_t Curr, n_t /* Succ */) bool GenFromZero = GeneratesFact && LLVMZeroValue::isLLVMZeroValue(Source); - if (GenFromZero || Source == PointerOp || PTS->count(Source)) { + if (GenFromZero || Source == PointerOp || PTS->contains(Source)) { return {Source, Load}; } @@ -614,34 +614,31 @@ auto IDEFeatureTaintAnalysis::initialSeeds() -> InitialSeeds { InitialSeeds Seeds; LLVMBasedCFG CFG; - forallStartingPoints(this->EntryPoints, IRDB, CFG, [this, &Seeds](n_t SP) { - // Set initial seeds at the required entry points and generate the global - // variables using generalized initial seeds - - // Generate zero value at the entry points - Seeds.addSeed(SP, this->getZeroValue(), 0); - // Generate formal parameters of entry points, e.g. main(). Formal - // parameters will otherwise cause trouble by overriding alloca - // instructions without being valid data-flow facts themselves. - - // Generate all global variables using generalized initial seeds - for (const auto &G : this->IRDB->getModule()->globals()) { - if (const auto *GV = llvm::dyn_cast(&G)) { - l_t InitialValues = TaintGen.getGeneratedTaintsAt(GV); - if (InitialValues.Taints.any()) { - Seeds.addSeed(SP, GV, std::move(InitialValues)); + forallStartingPoints( + this->EntryPoints, getProjectIRDB(), CFG, [this, &Seeds](n_t SP) { + // Set initial seeds at the required entry points and generate the + // global variables using generalized initial seeds + + // Generate zero value at the entry points + Seeds.addSeed(SP, this->getZeroValue(), 0); + // Generate formal parameters of entry points, e.g. main(). Formal + // parameters will otherwise cause trouble by overriding alloca + // instructions without being valid data-flow facts themselves. + + // Generate all global variables using generalized initial seeds + for (const auto &G : this->IRDB->getModule()->globals()) { + if (const auto *GV = llvm::dyn_cast(&G)) { + l_t InitialValues = TaintGen.getGeneratedTaintsAt(GV); + if (InitialValues.Taints.any()) { + Seeds.addSeed(SP, GV, std::move(InitialValues)); + } + } } - } - } - }); + }); return Seeds; } -bool IDEFeatureTaintAnalysis::isZeroValue(d_t FlowFact) const noexcept { - return LLVMZeroValue::isLLVMZeroValue(FlowFact); -} - void IDEFeatureTaintAnalysis::emitTextReport( GenericSolverResults SR, llvm::raw_ostream &OS) { OS << "\n====================== IDE-Inst-Interaction-Analysis Report " diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.cpp index 2223a97007..fcbe11fa30 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.cpp @@ -269,7 +269,7 @@ static_assert(is_llvm_hashable_v); IDELinearConstantAnalysis::IDELinearConstantAnalysis( const LLVMProjectIRDB *IRDB, const LLVMBasedICFG *ICF, std::vector EntryPoints) - : IDETabulationProblem(IRDB, std::move(EntryPoints), createZeroValue()), + : base_t(IRDB, std::move(EntryPoints), LLVMZeroValue::getInstance()), ICF(ICF) { assert(ICF != nullptr); } @@ -286,7 +286,7 @@ IDELinearConstantAnalysis::getNormalFlowFunction(n_t Curr, n_t /*Succ*/) { if (const auto *Alloca = llvm::dyn_cast(Curr)) { auto *AT = Alloca->getAllocatedType(); if (AT->isIntegerTy() || isIntegerLikeType(AT)) { - return generateFromZero(Alloca); + return generateFlow(Alloca, ZeroValue); } } @@ -397,7 +397,7 @@ IDELinearConstantAnalysis::initialSeeds() { InitialSeeds Seeds; forallStartingPoints(EntryPoints, ICF, [this, &Seeds](n_t SP) { - Seeds.addSeed(SP, getZeroValue(), bottomElement()); + Seeds.addSeed(SP, ZeroValue, Bottom{}); // Generate global integer-typed variables using generalized initial seeds for (const auto &G : IRDB->getModule()->globals()) { @@ -434,16 +434,6 @@ IDELinearConstantAnalysis::getSummaryFlowFunction(n_t Curr, f_t /*CalleeFun*/) { return nullptr; } -IDELinearConstantAnalysis::d_t -IDELinearConstantAnalysis::createZeroValue() const { - // create a special value to represent the zero value! - return LLVMZeroValue::getInstance(); -} - -bool IDELinearConstantAnalysis::isZeroValue(d_t Fact) const noexcept { - return LLVMZeroValue::isLLVMZeroValue(Fact); -} - // In addition provide specifications for the IDE parts EdgeFunction @@ -736,4 +726,6 @@ void IDELinearConstantAnalysis::LCAResult::print(llvm::raw_ostream &OS) const { } } +static_assert(IDEProblem); + } // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp index 7910d70061..a61639ee3b 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.cpp @@ -49,12 +49,11 @@ IFDSTaintAnalysis::IFDSTaintAnalysis(const LLVMProjectIRDB *IRDB, std::vector EntryPoints, bool TaintMainArgs, bool EnableStrongUpdateStore) - : IFDSTabulationProblem(IRDB, std::move(EntryPoints), createZeroValue()), + : IfdsIdeProblemMixin( + IRDB, std::move(EntryPoints), createZeroValue()), Config(Config), PT(PT), TaintMainArgs(TaintMainArgs), EnableStrongUpdateStore(EnableStrongUpdateStore), - Llvmfdff(library_summary::readFromFDFF( - getLibCSummary(), - [IRDB](llvm::StringRef FName) { return IRDB->getFunction(FName); })) { + Llvmfdff(library_summary::readFromFDFF(getLibCSummary(), *IRDB)) { assert(Config != nullptr); assert(PT); } @@ -77,7 +76,7 @@ bool IFDSTaintAnalysis::isSourceCall(const llvm::CallBase *CB, return false; } - if (AdditionalFacts.count(CB)) { + if (AdditionalFacts.contains(CB)) { return true; } @@ -105,7 +104,7 @@ bool IFDSTaintAnalysis::isSinkCall(const llvm::CallBase *CB, return false; } - if (AdditionalLeaks.count(CB)) { + if (AdditionalLeaks.contains(CB)) { return true; } @@ -318,8 +317,7 @@ auto IFDSTaintAnalysis::getNormalFlowFunction(n_t Curr, return lambdaFlow([this, Store, Ret = std::move(Ret)](d_t Source) { if (Store->getValueOperand() == Source) { if (Leaks[Store].insert(Source).second) { - Printer->onResult(Store, Source, - DataFlowAnalysisType::IFDSTaintAnalysis); + onResult(Store, Source, DataFlowAnalysisType::IFDSTaintAnalysis); } } @@ -483,7 +481,7 @@ auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, if (CS->hasStructRetAttr()) { const auto *SRet = CS->getArgOperand(0); - if (!Gen.count(SRet)) { + if (!Gen.contains(SRet)) { // SRet is guaranteed to be written to by the call. If it does not // generate it, we can freely kill it Kill.insert(SRet); @@ -493,14 +491,13 @@ auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, if (!Leak.empty() || !Kill.empty()) { return lambdaFlow([Leak{std::move(Leak)}, Kill{std::move(Kill)}, this, CallSite](d_t Source) -> container_type { - if (Leak.count(Source)) { + if (Leak.contains(Source)) { if (Leaks[CallSite].insert(Source).second) { - Printer->onResult(CallSite, Source, - DataFlowAnalysisType::IFDSTaintAnalysis); + onResult(CallSite, Source, DataFlowAnalysisType::IFDSTaintAnalysis); } } - if (Kill.count(Source)) { + if (Kill.contains(Source)) { return {}; } @@ -521,10 +518,9 @@ auto IFDSTaintAnalysis::getSummaryFlowFunction([[maybe_unused]] n_t CallSite, return Gen; } - if (Leak.count(Source)) { + if (Leak.contains(Source)) { if (Leaks[CallSite].insert(Source).second) { - Printer->onResult(CallSite, Source, - DataFlowAnalysisType::IFDSTaintAnalysis); + onResult(CallSite, Source, DataFlowAnalysisType::IFDSTaintAnalysis); } } @@ -541,7 +537,7 @@ auto IFDSTaintAnalysis::initialSeeds() -> InitialSeeds { Config->makeInitialSeeds(LLVMTaintConfig::SeedConfig::Arguments); LLVMBasedCFG C; - addSeedsForStartingPoints(EntryPoints, IRDB, C, Seeds, getZeroValue(), + addSeedsForStartingPoints(EntryPoints, IRDB.get(), C, Seeds, getZeroValue(), psr::BinaryDomain::BOTTOM); if (TaintMainArgs && llvm::is_contained(EntryPoints, "main")) { @@ -572,15 +568,11 @@ auto IFDSTaintAnalysis::createZeroValue() const -> d_t { return LLVMZeroValue::getInstance(); } -bool IFDSTaintAnalysis::isZeroValue(d_t FlowFact) const noexcept { - return LLVMZeroValue::isLLVMZeroValue(FlowFact); -} - void IFDSTaintAnalysis::emitTextReport( GenericSolverResults /*SR*/, llvm::raw_ostream &OS) { OS << "\n----- Found the following leaks -----\n"; - Printer->onFinalize(OS); + printer().onFinalize(OS); } bool IFDSTaintAnalysis::isInteresting( diff --git a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp index 0b9dab8418..7101e222ee 100644 --- a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp @@ -121,6 +121,8 @@ static AliasResult translateAAResult(llvm::AliasResult Res) noexcept { case llvm::AliasResult::MustAlias: return AliasResult::MustAlias; } + + llvm_unreachable("All alias result types handled in the switch above"); } static llvm::Type *getPointeeTypeOrNull(const llvm::Value *Ptr) { diff --git a/lib/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.cpp b/lib/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.cpp index 3776097c7e..05ddeb0f3d 100644 --- a/lib/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMPointerAssignmentGraph.cpp @@ -193,7 +193,7 @@ collectReachingDefs(llvm::MemoryAccess *MA, const llvm::MemorySSA &MSSA, } // namespace -struct [[clang::internal_linkage]] LLVMPAGBuilder::PAGBuildData { +struct PSR_INTERNAL_LINKAGE LLVMPAGBuilder::PAGBuildData { const llvm::DataLayout &DL; // NOLINT ValueCompressor &VC; // NOLINT const PAGMappedLibrarySummary &MLSum; // NOLINT @@ -820,9 +820,7 @@ struct [[clang::internal_linkage]] LLVMPAGBuilder::PAGBuildData { static const auto &getMappedLibSum( std::optional &MLSumBuf, const LLVMProjectIRDB &IRDB) { - MLSumBuf.emplace(library_summary::readFromFDFF( - getLibCSummary(), - [&IRDB](llvm::StringRef FName) { return IRDB.getFunction(FName); })); + MLSumBuf.emplace(library_summary::readFromFDFF(getLibCSummary(), IRDB)); return *MLSumBuf; } diff --git a/lib/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.cpp index f022e91bc4..37e34fd8e2 100644 --- a/lib/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMUnionFindAliasSet.cpp @@ -43,7 +43,7 @@ static inline bool isPotentialAllocSite(const llvm::Value *Val) { } template