From 392579d587b6bb08658dadd0ea900dada7e55c13 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 24 Mar 2025 18:32:17 +0100 Subject: [PATCH 01/24] Fix v2503 bugs (#763) * Remove googletest from gitmodules. Recommended way of using gtest is through FetchContent * Add to DOTGraph.cpp, which is missing on FreeBSD --- .gitmodules | 4 ---- lib/Utils/DOTGraph.cpp | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.gitmodules b/.gitmodules index 70653bf9e2..350885ac54 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,10 +2,6 @@ path = external/json url = https://github.com/nlohmann/json.git ignore = dirty -[submodule "lib/googletest"] - path = external/googletest - url = https://github.com/google/googletest.git - branch = master [submodule "external/json-schema-validator"] path = external/json-schema-validator url = https://github.com/pboettch/json-schema-validator.git diff --git a/lib/Utils/DOTGraph.cpp b/lib/Utils/DOTGraph.cpp index f745336c22..1bd7dc7d30 100644 --- a/lib/Utils/DOTGraph.cpp +++ b/lib/Utils/DOTGraph.cpp @@ -18,11 +18,11 @@ #include "nlohmann/json.hpp" -#include #include #include #include #include +#include #include namespace psr { From 9915caed0513c3f512aa8d5655795ff894ae7f5b Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 24 Mar 2025 18:55:49 +0100 Subject: [PATCH 02/24] Mark Hexastore as deprecated (#764) * Mark Hexastore as deprecated * minor --- include/phasar/DB/Hexastore.h | 3 ++- unittests/DB/CMakeLists.txt | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/include/phasar/DB/Hexastore.h b/include/phasar/DB/Hexastore.h index d6960c903c..64ade62375 100644 --- a/include/phasar/DB/Hexastore.h +++ b/include/phasar/DB/Hexastore.h @@ -73,7 +73,8 @@ struct HSResult { * * @brief Efficient data structure for holding graphs in databases. */ -class Hexastore { +class [[deprecated("This ancient API is not maintained for long and should not " + "be used anymore!")]] Hexastore { private: sqlite3 *HSInternalDB{}; static int callback(void * /*NotUsed*/, int Argc, char **Argv, diff --git a/unittests/DB/CMakeLists.txt b/unittests/DB/CMakeLists.txt index 87e1de139e..6f6c3606b6 100644 --- a/unittests/DB/CMakeLists.txt +++ b/unittests/DB/CMakeLists.txt @@ -1,9 +1,9 @@ -if(SQLite3_FOUND) - set(DBSources - HexastoreTest.cpp - ) +# if(SQLite3_FOUND) +# set(DBSources +# HexastoreTest.cpp +# ) - foreach(TEST_SRC ${DBSources}) - add_phasar_unittest(${TEST_SRC}) - endforeach(TEST_SRC) -endif() +# foreach(TEST_SRC ${DBSources}) +# add_phasar_unittest(${TEST_SRC}) +# endforeach(TEST_SRC) +# endif() From d294b68d12f2a0e0852f9f8efd7c63fa73102d38 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 24 Mar 2025 19:20:36 +0100 Subject: [PATCH 03/24] Fix init-submodules-release.sh (#765) * Fix init-submodules-release.sh * default to submodule update in the helper shell script * Fix pre-commit --------- Co-authored-by: Sriteja Kummita --- utils/init-submodules-release.sh | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/utils/init-submodules-release.sh b/utils/init-submodules-release.sh index 159e2f6201..31e4c9f3fd 100755 --- a/utils/init-submodules-release.sh +++ b/utils/init-submodules-release.sh @@ -3,13 +3,18 @@ source ./safeCommandsSet.sh if git submodule status 2>&1 | grep -iq "fatal: Not a git repository (or any of the parent directories): .git"; then + safe_cd "$(dirname "$0")"/../external/ -git clone git@github.com:google/googletest.git -safe_cd googletest/ -git checkout release-1.8.0 + +git clone --no-checkout https://github.com/nlohmann/json.git +safe_cd json/ +git checkout v3.11.3 safe_cd - -git clone git@github.com:nlohmann/json.git + +git clone --no-checkout https://github.com/pboettch/json-schema-validator.git safe_cd json/ -git checkout v3.4.0 +git checkout 2.3.0 safe_cd - + +else git submodule update --init; fi From 9e0f2ca46a60b1a86e1f39169e01b8f7c7c4de48 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Wed, 2 Apr 2025 18:14:04 +0200 Subject: [PATCH 04/24] Remove deprecated APIs and mark some others as deprecated (#766) --- CMakeLists.txt | 6 - Config.cmake.in | 14 - examples/use-phasar-as-library/CMakeLists.txt | 5 +- .../phasar/AnalysisStrategy/AnalysisSetup.h | 4 +- include/phasar/Config/Configuration.h | 23 - include/phasar/Config/Version.h | 12 - include/phasar/ControlFlow/CFGBase.h | 6 - include/phasar/ControlFlow/CallGraph.h | 24 - .../ControlFlow/CallGraphAnalysisType.def | 1 - include/phasar/ControlFlow/ICFGBase.h | 9 - .../phasar/DataFlow/IfdsIde/FlowFunctions.h | 365 -------------- .../DataFlow/IfdsIde/SpecialSummaries.h | 24 +- .../ControlFlow/LLVMBasedBackwardICFG.h | 4 - .../PhasarLLVM/ControlFlow/LLVMBasedICFG.h | 4 - .../ControlFlow/Resolver/DTAResolver.h | 78 --- .../ControlFlow/SparseLLVMBasedICFGView.h | 1 - .../TypeStateDescription.h | 2 +- .../Passes/GeneralStatisticsAnalysis.h | 107 ---- include/phasar/PhasarLLVM/Pointer.h | 5 +- .../PhasarLLVM/Pointer/LLVMAliasGraph.h | 275 ---------- .../phasar/PhasarLLVM/Pointer/LLVMAliasSet.h | 9 - .../Pointer/TypeGraphs/CachedTypeGraph.h | 109 ---- .../Pointer/TypeGraphs/LazyTypeGraph.h | 92 ---- .../PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h | 50 -- .../TypeHierarchy/DIBasedTypeHierarchy.h | 6 - .../TypeHierarchy/LLVMTypeHierarchy.h | 40 +- .../PhasarLLVM/TypeHierarchy/LLVMVFTable.h | 6 - include/phasar/Pointer/AliasInfo.h | 16 - include/phasar/TypeHierarchy/TypeHierarchy.h | 6 - include/phasar/TypeHierarchy/VFTable.h | 6 - include/phasar/Utils/Logger.h | 6 - include/phasar/Utils/Utilities.h | 10 - lib/Config/Configuration.cpp | 4 - .../ControlFlow/LLVMBasedBackwardICFG.cpp | 4 - lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp | 6 - .../ControlFlow/LLVMVFTableProvider.cpp | 1 + .../ControlFlow/Resolver/DTAResolver.cpp | 239 --------- .../ControlFlow/Resolver/Resolver.cpp | 7 - .../ControlFlow/SparseLLVMBasedICFGView.cpp | 4 - .../Passes/GeneralStatisticsAnalysis.cpp | 46 -- lib/PhasarLLVM/Pointer/LLVMAliasGraph.cpp | 469 ------------------ lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp | 27 - .../Pointer/TypeGraphs/CachedTypeGraph.cpp | 159 ------ .../Pointer/TypeGraphs/LazyTypeGraph.cpp | 104 ---- .../TypeHierarchy/DIBasedTypeHierarchy.cpp | 6 - .../TypeHierarchy/LLVMTypeHierarchy.cpp | 71 +-- lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp | 5 - lib/Utils/Logger.cpp | 13 - lib/Utils/Utilities.cpp | 13 - unittests/DB/HexastoreTest.cpp | 8 +- .../PhasarLLVM/ControlFlow/CMakeLists.txt | 1 - .../ControlFlow/LLVMBasedICFG_DTATest.cpp | 67 --- .../PhasarLLVM/TypeHierarchy/CMakeLists.txt | 1 - .../LLVMTypeHierarchySerializationTest.cpp | 5 + .../TypeHierarchy/LLVMTypeHierarchyTest.cpp | 5 + .../TypeHierarchy/TypeGraphTest.cpp | 417 ---------------- 56 files changed, 52 insertions(+), 2955 deletions(-) delete mode 100644 include/phasar/Config/Version.h delete mode 100644 include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h delete mode 100644 include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h delete mode 100644 include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h delete mode 100644 include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h delete mode 100644 include/phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h delete mode 100644 lib/PhasarLLVM/ControlFlow/Resolver/DTAResolver.cpp delete mode 100644 lib/PhasarLLVM/Pointer/LLVMAliasGraph.cpp delete mode 100644 lib/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.cpp delete mode 100644 lib/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.cpp delete mode 100644 unittests/PhasarLLVM/ControlFlow/LLVMBasedICFG_DTATest.cpp delete mode 100644 unittests/PhasarLLVM/TypeHierarchy/TypeGraphTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b8d993810..72e0a73b99 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -38,12 +38,6 @@ else() set(PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT OFF) endif() -option(PHASAR_EXPERIMENTAL_CXX20 "Build phasar in C++20 mode. This is an experimental feature" OFF) -if(PHASAR_EXPERIMENTAL_CXX20) - message(DEPRECATION "The option PHASAR_EXPERIMENTAL_CXX20 is deprecated and will be removed in a future version of PhASAR. Use CMAKE_CXX_STANDARD=20 instead.") - set(CMAKE_CXX_STANDARD 20) -endif() - set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) diff --git a/Config.cmake.in b/Config.cmake.in index e531879c5d..fce796ba73 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -61,23 +61,9 @@ foreach(component ${phasar_FIND_COMPONENTS}) endforeach() if (NOT DEFINED phasar_FOUND OR phasar_FOUND EQUAL TRUE) - foreach(component ${phasar_FIND_COMPONENTS}) - # For backwards compatibility -- will be removed with next release - add_library(phasar::phasar_${component} ALIAS phasar::${component}) - endforeach() - if (NOT phasar_FIND_COMPONENTS) list(APPEND PHASAR_NEEDED_LIBS phasar::phasar) # Default target add_library(phasar ALIAS phasar::phasar) endif() - - function(phasar_config executable) - message(DEPRECATION "The function 'phasar_config' is deprecated. Use target_link_libraries(${executable} PUBLIC phasar::phasar) instead!") - - target_link_libraries(${executable} - PUBLIC - ${PHASAR_NEEDED_LIBS} - ) - endfunction() endif() diff --git a/examples/use-phasar-as-library/CMakeLists.txt b/examples/use-phasar-as-library/CMakeLists.txt index 5246e3b823..bacf370078 100644 --- a/examples/use-phasar-as-library/CMakeLists.txt +++ b/examples/use-phasar-as-library/CMakeLists.txt @@ -17,10 +17,7 @@ add_executable(myphasartool myphasartool.cpp ) -# Old way using phasar_config (deprecated): -# phasar_config(myphasartool) - -# New way using target_link_libraries: +# Link your tool against phasar: target_link_libraries(myphasartool phasar::llvm_ifdside) # If find_package did not specify components: diff --git a/include/phasar/AnalysisStrategy/AnalysisSetup.h b/include/phasar/AnalysisStrategy/AnalysisSetup.h index dcdfb6ee2e..975ed1c292 100644 --- a/include/phasar/AnalysisStrategy/AnalysisSetup.h +++ b/include/phasar/AnalysisStrategy/AnalysisSetup.h @@ -12,7 +12,7 @@ #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" -#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" namespace psr { @@ -29,7 +29,7 @@ struct AnalysisSetup { struct DefaultAnalysisSetup : AnalysisSetup { using PointerAnalysisTy = LLVMAliasSet; using CallGraphAnalysisTy = LLVMBasedICFG; - using TypeHierarchyTy = LLVMTypeHierarchy; + using TypeHierarchyTy = DIBasedTypeHierarchy; }; } // namespace psr diff --git a/include/phasar/Config/Configuration.h b/include/phasar/Config/Configuration.h index 1d0f91b789..975e18aa58 100644 --- a/include/phasar/Config/Configuration.h +++ b/include/phasar/Config/Configuration.h @@ -68,29 +68,6 @@ class PhasarConfig { // NOLINTNEXTLINE(readability-identifier-naming) [[nodiscard]] static llvm::StringRef PhasarDirectory() noexcept; - /// Name of the file storing all standard header search paths used for - /// compilation. - [[nodiscard, deprecated("This ancient API is broken and should not be used " - "anymore")]] static constexpr llvm::StringRef - // NOLINTNEXTLINE(readability-identifier-naming) - HeaderSearchPathsFileName() noexcept { - return "standard_header_paths.conf"; - } - - /// Name of the compile_commands.json file (in case we wish to rename) - [[nodiscard, deprecated("This ancient API is broken and should not be used " - "anymore")]] static constexpr llvm::StringRef - // NOLINTNEXTLINE(readability-identifier-naming) - CompileCommandsJson() noexcept { - return "compile_commands.json"; - } - - /// Default Source- and Sink-Functions path - [[nodiscard, deprecated("This ancient API is broken and should not be used " - "anymore")]] static llvm::StringRef - // NOLINTNEXTLINE(readability-identifier-naming) - DefaultSourceSinkFunctionsPath() noexcept; - // Variables to be used in JSON export format /// Identifier for call graph export // NOLINTNEXTLINE(readability-identifier-naming) diff --git a/include/phasar/Config/Version.h b/include/phasar/Config/Version.h deleted file mode 100644 index c5017b860b..0000000000 --- a/include/phasar/Config/Version.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef PHASAR_CONFIG_VERSION_H -#define PHASAR_CONFIG_VERSION_H - -/// Note: This header is only left for backward compatibility and may be removed -/// in the future - -#pragma GCC warning( \ - "The header Version.h is deprecated. Use 'phasar/Config/phasar-config.h' instead") - -#include "phasar/Config/phasar-config.h" - -#endif diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index 0ec38d5dcb..6f0f1d610d 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -13,8 +13,6 @@ #include "phasar/Utils/ByRef.h" #include "phasar/Utils/TypeTraits.h" -#include "nlohmann/json.hpp" - namespace psr { enum class SpecialMemberFunctionType; @@ -131,10 +129,6 @@ template class CFGBase { void print(ByConstRef Fun, llvm::raw_ostream &OS) const { self().printImpl(Fun, OS); } - [[nodiscard, deprecated("Please use printAsJson() instead")]] nlohmann::json - getAsJson(ByConstRef Fun) const { - return self().getAsJsonImpl(Fun); - } protected: Derived &self() noexcept { return static_cast(*this); } diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index d73a1b5783..e2a03fec25 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -19,10 +19,7 @@ #include "llvm/IR/Function.h" -#include "nlohmann/json.hpp" - #include -#include #include #include @@ -105,27 +102,6 @@ class CallGraph : public CallGraphBase> { CGData.printAsJson(OS); } - /// Creates a JSON representation of this call-graph suitable for presistent - /// storage. - /// Use the ctor taking a json object for deserialization - template - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson(FunctionIdGetter GetFunctionId, - InstIdGetter GetInstructionId) const { - nlohmann::json J; - - for (const auto &[Fun, Callers] : CallersOf) { - auto &JCallers = J[std::invoke(GetFunctionId, Fun)]; - - for (const auto &CS : *Callers) { - JCallers.push_back(std::invoke(GetInstructionId, CS)); - } - } - - return J; - } - template void printAsDot(llvm::raw_ostream &OS, FunctionLabelGetter GetFunctionLabel, diff --git a/include/phasar/ControlFlow/CallGraphAnalysisType.def b/include/phasar/ControlFlow/CallGraphAnalysisType.def index 3430b21c07..6576b08318 100644 --- a/include/phasar/ControlFlow/CallGraphAnalysisType.def +++ b/include/phasar/ControlFlow/CallGraphAnalysisType.def @@ -14,7 +14,6 @@ CALL_GRAPH_ANALYSIS_TYPE(NORESOLVE, "nores", "Don't resolve indirect- and virtual calls") CALL_GRAPH_ANALYSIS_TYPE(CHA, "cha", "Class hierarchy analysis") CALL_GRAPH_ANALYSIS_TYPE(RTA, "rta", "Rapid type analysis") -CALL_GRAPH_ANALYSIS_TYPE(DTA, "dta", "Declared type analysis") CALL_GRAPH_ANALYSIS_TYPE(VTA, "vta", "Variable type analysis") CALL_GRAPH_ANALYSIS_TYPE(OTF, "otf", "On-the-fly analysis based on points-to info (default)") diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index bbdd5b75f2..4ef63126d1 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -17,8 +17,6 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include namespace psr { @@ -112,13 +110,6 @@ template class ICFGBase { self().printAsJsonImpl(OS); } - /// Returns the underlying call-graph as JSON - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const { - return self().getAsJsonImpl(); - } - [[nodiscard]] size_t getNumCallSites() const noexcept { return self().getNumCallSitesImpl(); } diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index c416571c2f..2f3fa53f5c 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -29,7 +29,6 @@ #include #include #include -#include namespace psr { @@ -872,370 +871,6 @@ class FlowFunctions } }; -//////////////////////////////////////////////////////////////////////////////// -// Legacy Flow Functions -//////////////////////////////////////////////////////////////////////////////// - -template > -class [[deprecated("Use identityFlow() instead")]] Identity - : public FlowFunction { -public: - using typename FlowFunction::FlowFunctionType; - using typename FlowFunction::FlowFunctionPtrType; - - using typename FlowFunction::container_type; - - ~Identity() override = default; - Identity(const Identity &I) = delete; - Identity &operator=(const Identity &I) = delete; - // simply return what the user provides - container_type computeTargets(D Source) override { return {Source}; } - static std::shared_ptr getInstance() { - static std::shared_ptr Instance = - std::shared_ptr(new Identity); - return Instance; - } - -private: - Identity() = default; -}; - -template > -class [[deprecated("Use lambdaFlow() instead")]] LambdaFlow - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - LambdaFlow(Fn && F) : Flow(std::move(F)) {} - LambdaFlow(const Fn &F) : Flow(F) {} - ~LambdaFlow() override = default; - container_type computeTargets(D Source) override { return Flow(Source); } - -private: - // std::function flow; - Fn Flow; -}; - -template > -typename FlowFunction::FlowFunctionPtrType makeLambdaFlow(Fn &&F) { - return std::make_shared, Container>>( - std::forward(F)); -} - -template > -class [[deprecated]] Compose : public FlowFunction { -public: - using typename FlowFunction::FlowFunctionType; - using typename FlowFunction::FlowFunctionPtrType; - - using typename FlowFunction::container_type; - - Compose(const std::vector &Funcs) : Funcs(Funcs) {} - - ~Compose() override = default; - - container_type computeTargets(D Source) override { - container_type Current{Source}; - for (const FlowFunctionPtrType &Func : Funcs) { - container_type Next; - for (const D &Fact : Current) { - container_type Target = Func->computeTargets(Fact); - Next.insert(Target.begin(), Target.end()); - } - Current = Next; - } - return Current; - } - - static FlowFunctionPtrType - compose(const std::vector &Funcs) { - std::vector Vec; - for (const FlowFunctionPtrType &Func : Funcs) { - if (Func != Identity::getInstance()) { - Vec.push_back(Func); - } - } - if (Vec.size() == 1) { // NOLINT(readability-container-size-empty) - return Vec[0]; - } - if (Vec.empty()) { - return Identity::getInstance(); - } - return std::make_shared(Vec); - } - -protected: - const std::vector Funcs; -}; - -//===----------------------------------------------------------------------===// -// Gen flow functions - -template > -class [[deprecated("Use generateFlow() instead")]] Gen - : public FlowFunction { - using typename FlowFunction::container_type; - -protected: - D GenValue; - D ZeroValue; - -public: - Gen(D GenValue, D ZeroValue) : GenValue(GenValue), ZeroValue(ZeroValue) {} - ~Gen() override = default; - - container_type computeTargets(D Source) override { - if (Source == ZeroValue) { - return {Source, GenValue}; - } - return {Source}; - } -}; - -/** - * @brief Generates the given value if the given predicate evaluates to true. - * @tparam D The type of data-flow facts to be generated. - */ -template > -class [[deprecated("Use generateFlowIf() instead")]] GenIf - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - GenIf(D GenValue, std::function Predicate) - : GenValues({GenValue}), Predicate(std::move(Predicate)) {} - - GenIf(container_type GenValues, std::function Predicate) - : GenValues(std::move(GenValues)), Predicate(std::move(Predicate)) {} - - ~GenIf() override = default; - - container_type computeTargets(D Source) override { - if (Predicate(Source)) { - container_type ToGenerate; - ToGenerate.insert(Source); - ToGenerate.insert(GenValues.begin(), GenValues.end()); - return ToGenerate; - } - return {Source}; - } - -protected: - container_type GenValues; - std::function Predicate; -}; - -template > -class [[deprecated("Use generateManyFlows() instead")]] GenAll - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - GenAll(container_type GenValues, D ZeroValue) - : GenValues(std::move(GenValues)), ZeroValue(ZeroValue) {} - ~GenAll() override = default; - container_type computeTargets(D Source) override { - if (Source == ZeroValue) { - GenValues.insert(Source); - return GenValues; - } - return {Source}; - } - -protected: - container_type GenValues; - D ZeroValue; -}; - -//===----------------------------------------------------------------------===// -// Kill flow functions - -template > -class [[deprecated("Use killFlow() instead")]] Kill - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - Kill(D KillValue) : KillValue(KillValue) {} - ~Kill() override = default; - container_type computeTargets(D Source) override { - if (Source == KillValue) { - return {}; - } - return {Source}; - } - -protected: - D KillValue; -}; - -/// \brief Kills all facts for which the given predicate evaluates to true. -/// \tparam D The type of data-flow facts to be killed. -template > -class [[deprecated("Use killFlowIf instead")]] KillIf - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - KillIf(std::function Predicate) : Predicate(std::move(Predicate)) {} - ~KillIf() override = default; - container_type computeTargets(D Source) override { - if (Predicate(Source)) { - return {}; - } - return {Source}; - } - -protected: - std::function Predicate; -}; - -template > -class [[deprecated("Use killManyFlows() instead")]] KillMultiple - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - KillMultiple(std::set KillValues) : KillValues(std::move(KillValues)) {} - ~KillMultiple() override = default; - container_type computeTargets(D Source) override { - if (KillValues.find(Source) != KillValues.end()) { - return {}; - } - return {Source}; - } - -protected: - container_type KillValues; -}; - -template > -class [[deprecated("Use killAllFlows() instead")]] KillAll - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - ~KillAll() override = default; - KillAll(const KillAll &K) = delete; - KillAll &operator=(const KillAll &K) = delete; - container_type computeTargets(D /*Source*/) override { - return container_type(); - } - - static std::shared_ptr> getInstance() { - static std::shared_ptr Instance = - std::shared_ptr(new KillAll); - return Instance; - } - -private: - KillAll() = default; -}; - -//===----------------------------------------------------------------------===// -// Gen-and-kill flow functions -template > -class [[deprecated( - "Use generateFlowAndKillAllOthers() instead")]] GenAndKillAllOthers - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - GenAndKillAllOthers(D GenValue, D ZeroValue) - : GenValue(GenValue), ZeroValue(ZeroValue) {} - ~GenAndKillAllOthers() override = default; - container_type computeTargets(D Source) override { - if (Source == ZeroValue) { - return {ZeroValue, GenValue}; - } - return {}; - } - -private: - D GenValue; - D ZeroValue; -}; - -template > -class [[deprecated( - "Use generateManyFlowsAndKillAllOthers() instead")]] GenAllAndKillAllOthers - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - GenAllAndKillAllOthers(const container_type &GenValues, D ZeroValue) - : GenValues(GenValues), ZeroValue(ZeroValue) {} - ~GenAllAndKillAllOthers() override = default; - container_type computeTargets(D Source) override { - if (Source == ZeroValue) { - GenValues.insert(Source); - return GenValues; - } - return {}; - } - -protected: - container_type GenValues; - D ZeroValue; -}; - -//===----------------------------------------------------------------------===// -// Miscellaneous flow functions - -template > -class [[deprecated("Use transferFlow() instead")]] Transfer - : public FlowFunction { -public: - using typename FlowFunction::container_type; - - Transfer(D ToValue, D FromValue) : ToValue(ToValue), FromValue(FromValue) {} - ~Transfer() override = default; - container_type computeTargets(D Source) override { - if (Source == FromValue) { - return {Source, ToValue}; - } - if (Source == ToValue) { - return {}; - } - return {Source}; - } - -protected: - D ToValue; - D FromValue; -}; - -template > -class [[deprecated("Use unionFlows() instead")]] Union - : public FlowFunction { -public: - using typename FlowFunction::container_type; - using typename FlowFunction::FlowFunctionType; - using typename FlowFunction::FlowFunctionPtrType; - - Union(const std::vector &FlowFuncs) - : FlowFuncs([&FlowFuncs]() { - if (FlowFuncs.empty()) { - return std::vector( - {Identity::getInstance()}); - } - return FlowFuncs; - }()) {} - - ~Union() override = default; - container_type computeTargets(D Source) override { - container_type Result; - for (const auto &FlowFunc : FlowFuncs) { - container_type Target = FlowFunc->computeTargets(Source); - Result.insert(Target.begin(), Target.end()); - } - return Result; - } - -protected: - const std::vector FlowFuncs; -}; - } // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h index 3154c52b33..41cb4d58e1 100644 --- a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h +++ b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h @@ -34,7 +34,9 @@ namespace psr { -template class SpecialSummaries { +template +class [[deprecated("This ancient API is not maintained and should not be used " + "anymore")]] SpecialSummaries { using FlowFunctionType = FlowFunction; using FlowFunctionPtrType = std::shared_ptr>; @@ -50,8 +52,8 @@ template class SpecialSummaries { // insert default flow and edge functions for (const auto &FunctionName : PhasarConfig::getPhasarConfig().specialFunctionNames()) { - SpecialFlowFunctions.insert( - std::make_pair(FunctionName, Identity::getInstance())); + // SpecialFlowFunctions.insert( + // std::make_pair(FunctionName, Identity::getInstance())); SpecialEdgeFunctions.insert( std::make_pair(FunctionName, EdgeIdentity{})); } @@ -95,8 +97,8 @@ template class SpecialSummaries { return SpecialFlowFunctions.count(Name); } - FlowFunctionPtrType - getSpecialFlowFunctionSummary(const llvm::Function *Func) { + FlowFunctionPtrType getSpecialFlowFunctionSummary( + const llvm::Function *Func) { return getSpecialFlowFunctionSummary(Func->getName()); } @@ -104,18 +106,18 @@ template class SpecialSummaries { return SpecialFlowFunctions[Name]; } - std::shared_ptr> - getSpecialEdgeFunctionSummary(const llvm::Function *Func) { + std::shared_ptr> getSpecialEdgeFunctionSummary( + const llvm::Function *Func) { return getSpecialEdgeFunctionSummary(Func->getName()); } - std::shared_ptr> - getSpecialEdgeFunctionSummary(const std::string &Name) { + std::shared_ptr> getSpecialEdgeFunctionSummary( + const std::string &Name) { return SpecialEdgeFunctions[Name]; } - friend llvm::raw_ostream & - operator<<(llvm::raw_ostream &OS, const SpecialSummaries &SpecialSumms) { + friend llvm::raw_ostream &operator<<( + llvm::raw_ostream &OS, const SpecialSummaries &SpecialSumms) { OS << "SpecialSummaries:\n"; for (auto &Entry : SpecialSumms.SpecialFunctionNames) { OS << Entry.first << " "; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h index c3ac4191b6..d9bbb1786a 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h @@ -46,9 +46,6 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, using ICFGBase::printAsJson; - using CFGBase::getAsJson; - using ICFGBase::getAsJson; - public: LLVMBasedBackwardICFG(LLVMBasedICFG *ForwardICFG); @@ -67,7 +64,6 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; void printAsJsonImpl(llvm::raw_ostream &OS) const; - [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; [[nodiscard]] size_t getNumCallSitesImpl() const noexcept; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h index 4b9e35dd9e..9ed1799e0b 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h @@ -135,9 +135,6 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { using ICFGBase::printAsJson; - using CFGBase::getAsJson; - using ICFGBase::getAsJson; - private: [[nodiscard]] FunctionRange getAllFunctionsImpl() const; [[nodiscard]] f_t getFunctionImpl(llvm::StringRef Fun) const; @@ -150,7 +147,6 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; void printAsJsonImpl(llvm::raw_ostream &OS) const; - [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; [[nodiscard]] const LLVMBasedCallGraph &getCallGraphImpl() const noexcept { return CG; } diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h deleted file mode 100644 index 3a34ea4f24..0000000000 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h +++ /dev/null @@ -1,78 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * DTAResolver.h - * - * Created on: 20.07.2018 - * Author: nicolas bellec - */ - -#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_RESOLVER_DTARESOLVER_H_ -#define PHASAR_PHASARLLVM_CONTROLFLOW_RESOLVER_DTARESOLVER_H_ - -#include "phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h" -#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" -// To switch the TypeGraph -// #include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" - -#include - -namespace llvm { -class Instruction; -class CallBase; -class Function; -class BitCastInst; -} // namespace llvm - -namespace psr { - -class [[deprecated("Does not work with opaque pointers anymore")]] DTAResolver - : public CHAResolver { -public: - using TypeGraph_t = CachedTypeGraph; - - DTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, - const DIBasedTypeHierarchy *TH); - - ~DTAResolver() override = default; - - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; - - void otherInst(const llvm::Instruction *Inst) override; - - [[nodiscard]] std::string str() const override; - - [[nodiscard]] bool mutatesHelperAnalysisInformation() - const noexcept override { - return false; - } - -protected: - TypeGraph_t TypeGraph; - - /** - * An heuristic that return true if the bitcast instruction is interesting to - * take into the DTA relational graph - */ - static bool heuristicAntiConstructorThisType( - const llvm::BitCastInst *BitCast); - - /** - * Another heuristic that return true if the bitcast instruction is - * interesting to take into the DTA relational graph (use the presence or not - * of vtable) - - */ - bool heuristicAntiConstructorVtablePos(const llvm::BitCastInst *BitCast); -}; -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h index 927840d252..8c0aaa9dac 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h @@ -59,7 +59,6 @@ class SparseLLVMBasedICFGView [[nodiscard]] llvm::SmallVector getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; - [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; [[nodiscard]] const SparseLLVMBasedCFG & diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h index a7e14e8fc0..0e6865686b 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h @@ -78,7 +78,7 @@ struct TypeStateDescription : public TypeStateDescriptionBase { * Represents the start/initial state of an object after creation, e.g. state * of a file handle after fopen() */ - [[nodiscard]] virtual State start() const = 0; + [[nodiscard, deprecated]] virtual State start() const = 0; /** * Represents the error state of an object diff --git a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h index 4f0e943b78..fddf1e98d5 100644 --- a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h +++ b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h @@ -19,8 +19,6 @@ #include "llvm/IR/PassManager.h" -#include "nlohmann/json.hpp" - #include namespace llvm { @@ -74,111 +72,6 @@ struct GeneralStatistics { std::set RetResInstructions; std::string ModuleName{}; - /** - * @brief Returns the number of Allocation sites. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use AllocationSites instead")]] size_t - getAllocationsites() const; - - /** - * @brief Returns the number of Function calls. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use CallSites instead")]] size_t - getFunctioncalls() const; - - /** - * @brief Returns the number of Instructions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use Instructions instead")]] size_t - getInstructions() const; - - /** - * @brief Returns the number of global pointers. - */ - [[nodiscard]] [[deprecated( - "All globals are pointers. Use Globals instead")]] size_t - getGlobalPointers() const; - - /** - * @brief Returns the number of basic blocks. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use BasicBlocks instead")]] size_t - getBasicBlocks() const; - - /** - * @brief Returns the number of functions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use Functions instead")]] size_t - getFunctions() const; - - /** - * @brief Returns the number of globals. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use Globals instead")]] size_t - getGlobals() const; - - /** - * @brief Returns the number of constant globals. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use GlobalConsts instead")]] size_t - getGlobalConsts() const; - - /** - * @brief Returns the number of memory intrinsics. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use MemIntrinsics instead")]] size_t - getMemoryIntrinsics() const; - - /** - * @brief Returns the number of store instructions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use StoreInstructions instead")]] size_t - getStoreInstructions() const; - - /** - * @brief Returns the number of load instructions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use LoadInstructions instead; this " - "function seems to be broken anyway")]] size_t - getLoadInstructions(); - - /** - * @brief Returns all possible Types. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use AllocatedTypes instead")]] const std:: - set & - getAllocatedTypes() const; - - /** - * @brief Returns all stack and heap allocating instructions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use AllocaInstructions " - "instead")]] const std::set & - getAllocaInstructions() const; - - /** - * @brief Returns all Return and Resume Instructions. - */ - [[nodiscard]] [[deprecated( - "Getters are no longer needed. Use RetResInstructions " - "instead")]] const std::set & - getRetResInstructions() const; - - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; }; diff --git a/include/phasar/PhasarLLVM/Pointer.h b/include/phasar/PhasarLLVM/Pointer.h index 220567251a..a2a67fd202 100644 --- a/include/phasar/PhasarLLVM/Pointer.h +++ b/include/phasar/PhasarLLVM/Pointer.h @@ -10,13 +10,10 @@ #ifndef PHASAR_PHASARLLVM_POINTER_H #define PHASAR_PHASARLLVM_POINTER_H -#include "phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h" +#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h" #endif // PHASAR_PHASARLLVM_POINTER_H diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h deleted file mode 100644 index 83c22dbef4..0000000000 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h +++ /dev/null @@ -1,275 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -#ifndef PHASAR_PHASARLLVM_POINTER_LLVMALIASGRAPH_H_ -#define PHASAR_PHASARLLVM_POINTER_LLVMALIASGRAPH_H_ - -#include "phasar/Config/Configuration.h" -#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" -#include "phasar/Pointer/AliasInfoBase.h" -#include "phasar/Pointer/AliasInfoTraits.h" -#include "phasar/Pointer/AliasSetOwner.h" -#include "phasar/Utils/AnalysisProperties.h" - -#include "llvm/IR/AbstractCallSite.h" - -#include "boost/graph/adjacency_list.hpp" -#include "nlohmann/json.hpp" - -#include -#include -#include - -namespace llvm { -class Value; -class Module; -class Instruction; -class AAResults; -class Function; -class Type; -} // namespace llvm - -namespace psr { -extern template class BoxedPtr; -extern template class BoxedConstPtr; -extern template class AliasSetOwner; - -class LLVMAliasGraph; -template <> -struct AliasInfoTraits - : DefaultAATraits {}; - -/** - * TODO: Is this impl legacy code? Can it be removed? - * - * This class is a representation of a points-to graph. It is possible to - * construct a points-to graph for a single function using the results of - * the llvm alias analysis or merge several points-to graphs into a single - * points-to graph, e.g. to construct a whole program points-to graph. - * - * The graph itself is undirectional and can have labeled edges. - * - * @brief Represents the points-to graph of a function. - */ -class [[deprecated("Use LLVMAliasSet Instead")]] LLVMAliasGraph - : public AnalysisPropertiesMixin, - AliasInfoBaseUtils { - using traits_t = AliasInfoTraits; - -public: - using n_t = traits_t::n_t; - using v_t = traits_t::v_t; - using AliasSetTy = traits_t::AliasSetTy; - using AliasSetPtrTy = traits_t::AliasSetPtrTy; - using AllocationSiteSetPtrTy = traits_t::AllocationSiteSetPtrTy; - - // Call-graph friends - friend class LLVMBasedICFG; - /** - * @brief Holds the information of a vertex in the points-to graph. - */ - struct VertexProperties { - /** - * This might be an Instruction, an Operand of an Instruction, Global - * Variable or a formal Argument. - */ - const llvm::Value *V = nullptr; - VertexProperties() = default; - VertexProperties(const llvm::Value *V); - std::string getValueAsString() const; - - // Fetching the users for V is expensive, so we cache the result. - mutable std::vector Users; - std::vector getUsers() const; - }; - - /** - * @brief Holds the information of an edge in the points-to graph. - */ - struct EdgeProperties { - /// This may contain a call or invoke instruction. - const llvm::Value *V = nullptr; - EdgeProperties() = default; - EdgeProperties(const llvm::Value *V); - [[nodiscard]] std::string getValueAsString() const; - }; - - /// Data structure for holding the points-to graph. - using graph_t = - boost::adjacency_list; - - /// The type for vertex representative objects. - using vertex_t = boost::graph_traits::vertex_descriptor; - - /// The type for edge representative objects. - using edge_t = boost::graph_traits::edge_descriptor; - - /// The type for a vertex iterator. - using vertex_iterator = boost::graph_traits::vertex_iterator; - using out_edge_iterator = boost::graph_traits::out_edge_iterator; - using in_edge_iterator = boost::graph_traits::in_edge_iterator; - - /** - * Creates a points-to graph based on the computed Alias results. - * - * @brief Creates a points-to graph for a given function. - * @param AA Contains the computed Alias Results. - * @param F Points-to graph is created for this particular function. - * @param onlyConsiderMustAlias True, if only Must Aliases should be - * considered. False, if May and Must Aliases should be - * considered. - */ - explicit LLVMAliasGraph(LLVMProjectIRDB & IRDB, bool UseLazyEvaluation = true, - AliasAnalysisType PATy = - AliasAnalysisType::CFLAnders); - - /** - * @brief Returns true if graph contains 0 nodes. - */ - bool empty() const; - - /** - * @brief Returns the number of graph nodes. - */ - size_t size() const; - - /** - * @brief Returns a std::vector containing pointers which are escaping through - * function parameters. - * @return Vector holding function argument pointers and the function argument - * number. - */ - std::vector> - getPointersEscapingThroughParams(); - - /** - * @brief Returns a std::vector containing pointers which are escaping through - * function return statements. - * @return Vector with pointers. - */ - std::vector getPointersEscapingThroughReturns() const; - - /** - * @brief Returns a std::vector containing pointers which are escaping through - * function return statements for a specific function. - * @param F Function pointer - * @return Vector with pointers. - */ - std::vector getPointersEscapingThroughReturnsForFunction( - const llvm::Function *Fd) const; - - /** - * @brief Checks if a given value is represented by a vertex in the points-to - * graph. - * @return True, the points-to graph contains the given value. - */ - bool containsValue(llvm::Value * V); - - /** - * The value-vertex-map maps each Value of the points-to graph to - * its corresponding Vertex in the points-to graph. - * - * @brief Prints the value-vertex-map to the command-line. - */ - void printValueVertexMap(); - - class PointerVertexOrEdgePrinter { - public: - PointerVertexOrEdgePrinter(const graph_t &PAG) : PAG(PAG) {} - template - void operator()(std::ostream &Out, const VertexOrEdge &V) const { - Out << "[label=\"" << PAG[V].getValueAsString() << "\"]"; - } - - private: - const graph_t &PAG; - }; - - static inline PointerVertexOrEdgePrinter makePointerVertexOrEdgePrinter( - const graph_t &PAG) { - return {PAG}; - } - - /** - * @brief Prints the points-to graph in .dot format to the given output - * stream. - * @param outputstream. - */ - void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; - - size_t getNumVertices() const; - - size_t getNumEdges() const; - - // --- IsAliasInfo impl - - [[nodiscard]] bool isInterProcedural() const noexcept; - - [[nodiscard]] AliasAnalysisType getAliasAnalysisType() const noexcept; - - AliasResult alias(const llvm::Value *V1, const llvm::Value *V2, - const llvm::Instruction *I = nullptr); - - AliasSetPtrTy getAliasSet(const llvm::Value *V, - const llvm::Instruction *I = nullptr); - - AllocationSiteSetPtrTy getReachableAllocationSites( - const llvm::Value *V, bool IntraProcOnly = false, - const llvm::Instruction *I = nullptr); - - [[nodiscard]] bool isInReachableAllocationSites( - const llvm::Value *V, const llvm::Value *PotentialValue, - bool IntraProcOnly = false, const llvm::Instruction *I = nullptr); - - void mergeWith(const LLVMAliasGraph &OtherPTI); - - void introduceAlias(const llvm::Value *V1, const llvm::Value *V2, - const llvm::Instruction *I = nullptr, - AliasResult Kind = AliasResult::MustAlias); - - void print(llvm::raw_ostream &OS = llvm::outs()) const; - - [[nodiscard]] nlohmann::json getAsJson() const; - - void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; - - [[nodiscard]] AnalysisProperties getAnalysisProperties() const noexcept { - return AnalysisProperties::None; - } - -private: - // --- - - // void mergeGraph(const LLVMAliasGraph &Other); - - void computeAliasGraph(const llvm::Value *V); - - void computeAliasGraph(llvm::Function * F); - - struct AllocationSiteDFSVisitor; - struct ReachabilityDFSVisitor; - - /// The points to graph. - graph_t PAG; - using ValueVertexMapT = std::unordered_map; - ValueVertexMapT ValueVertexMap; - /// Keep track of what has already been merged into this points-to graph. - std::unordered_set AnalyzedFunctions; - LLVMBasedAliasAnalysis PTA; - - AliasSetOwner::memory_resource_type MRes; - AliasSetOwner Owner{&MRes}; - std::unordered_map> Cache; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index 7a9c2dbd71..ff4ccbe5dc 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -17,15 +17,10 @@ #include "phasar/Pointer/AliasResult.h" #include "phasar/Pointer/AliasSetOwner.h" #include "phasar/Utils/AnalysisProperties.h" -#include "phasar/Utils/StableVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" -#include "nlohmann/json.hpp" - -#include - namespace llvm { class Value; class Instruction; @@ -100,10 +95,6 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, void print(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const; - [[nodiscard]] LLVMAliasSetData getLLVMAliasSetData() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; diff --git a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h deleted file mode 100644 index 17efc1abbf..0000000000 --- a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h +++ /dev/null @@ -1,109 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * CachedTypeGraph.h - * - * Created on: 28.06.2018 - * Author: nicolas bellec - */ - -#ifndef PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_CACHEDTYPEGRAPH_H_ -#define PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_CACHEDTYPEGRAPH_H_ - -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h" - -#include "boost/graph/adjacency_list.hpp" -#include "boost/graph/graph_traits.hpp" -#include "boost/graph/reverse_graph.hpp" - -#include -#include -#include - -#ifndef PSR_FRIEND_TEST -#define PSR_FRIEND_TEST(TEST, CLASS) -#endif - -namespace llvm { -class StructType; -} // namespace llvm - -namespace psr { -class CachedTypeGraph : public TypeGraph { -protected: - struct VertexProperties { - std::string Name; - std::set Types; - const llvm::StructType *BaseType = nullptr; - }; - - struct EdgeProperties { - EdgeProperties() = default; - }; - - using graph_t = - boost::adjacency_list; - - using vertex_t = boost::graph_traits::vertex_descriptor; - using vertex_iterator = boost::graph_traits::vertex_iterator; - using edge_t = boost::graph_traits::edge_descriptor; - using out_edge_iterator = boost::graph_traits::out_edge_iterator; - using in_edge_iterator = boost::graph_traits::in_edge_iterator; - - using rev_graph_t = boost::reverse_graph; - - using rev_vertex_t = boost::graph_traits::vertex_descriptor; - using rev_vertex_iterator = boost::graph_traits::vertex_iterator; - using rev_edge_t = boost::graph_traits::edge_descriptor; - using rev_out_edge_iterator = - boost::graph_traits::out_edge_iterator; - using rev_in_edge_iterator = - boost::graph_traits::in_edge_iterator; - - struct dfs_visitor; - struct reverse_type_propagation_dfs_visitor; - - std::unordered_map TypeVertexMap; - graph_t G; - bool AlreadyVisited = false; - - PSR_FRIEND_TEST(TypeGraphTest, AddType) - PSR_FRIEND_TEST(TypeGraphTest, AddLinkSimple) - PSR_FRIEND_TEST(TypeGraphTest, AddLinkWithSubs) - PSR_FRIEND_TEST(TypeGraphTest, AddLinkWithRecursion) - PSR_FRIEND_TEST(TypeGraphTest, ReverseTypePropagation) - PSR_FRIEND_TEST(TypeGraphTest, TypeAggregation) - PSR_FRIEND_TEST(TypeGraphTest, Merging) - - vertex_t addType(const llvm::StructType *NewType); - void reverseTypePropagation(const llvm::StructType *BaseStruct); - void aggregateTypes(); - bool addLinkWithoutReversePropagation(const llvm::StructType *From, - const llvm::StructType *To); - -public: - CachedTypeGraph() = default; - - ~CachedTypeGraph() override = default; - // CachedTypeGraph(const CachedTypeGraph ©) = delete; - // CachedTypeGraph& operator=(const CachedTypeGraph ©) = delete; - // CachedTypeGraph(CachedTypeGraph &&move) = delete; - // CachedTypeGraph& operator=(CachedTypeGraph &&move) = delete; - - bool addLink(const llvm::StructType *From, - const llvm::StructType *To) override; - void printAsDot(const std::string &Path = "typegraph.dot") const override; - std::set - getTypes(const llvm::StructType *StructType) override; -}; -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h deleted file mode 100644 index a29e6a45ea..0000000000 --- a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h +++ /dev/null @@ -1,92 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * LazyTypeGraph.h - * - * Created on: 28.06.2018 - * Author: nicolas bellec - */ - -#ifndef PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_LAZYTYPEGRAPH_H -#define PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_LAZYTYPEGRAPH_H - -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h" - -#include "boost/graph/adjacency_list.hpp" -#include "boost/graph/graph_traits.hpp" -#include "boost/graph/reverse_graph.hpp" - -#include -#include -#include - -namespace llvm { -class StructType; -} // namespace llvm - -namespace psr { -class LazyTypeGraph : public TypeGraph { -protected: - struct VertexProperties { - std::string Name; - const llvm::StructType *SType = nullptr; - }; - - struct EdgeProperties { - EdgeProperties() = default; - }; - - using graph_t = - boost::adjacency_list; - - using vertex_t = boost::graph_traits::vertex_descriptor; - using vertex_iterator = boost::graph_traits::vertex_iterator; - using edge_t = boost::graph_traits::edge_descriptor; - using out_edge_iterator = boost::graph_traits::out_edge_iterator; - using in_edge_iterator = boost::graph_traits::in_edge_iterator; - - using rev_graph_t = boost::reverse_graph; - - using rev_vertex_t = boost::graph_traits::vertex_descriptor; - using rev_vertex_iterator = boost::graph_traits::vertex_iterator; - using rev_edge_t = boost::graph_traits::edge_descriptor; - using rev_out_edge_iterator = - boost::graph_traits::out_edge_iterator; - using rev_in_edge_iterator = - boost::graph_traits::in_edge_iterator; - - struct dfs_visitor; - - std::unordered_map TypeToVertexMap; - graph_t Graph; - bool AlreadyVisited = false; - - vertex_t addType(const llvm::StructType *NewType); - void aggregateTypes(); - -public: - LazyTypeGraph() = default; - - ~LazyTypeGraph() override = default; - // LazyTypeGraph(const LazyTypeGraph ©) = delete; - // LazyTypeGraph& operator=(const LazyTypeGraph ©) = delete; - // LazyTypeGraph(LazyTypeGraph &&move) = delete; - // LazyTypeGraph& operator=(LazyTypeGraph &&move) = delete; - - [[nodiscard]] bool addLink(const llvm::StructType *From, - const llvm::StructType *To) override; - void printAsDot(const std::string &Path = "typegraph.dot") const override; - [[nodiscard]] std::set - getTypes(const llvm::StructType *StructType) override; -}; -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h deleted file mode 100644 index b7a4ac9090..0000000000 --- a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/TypeGraph.h +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * TypeGraph.h - * - * Created on: 28.06.2018 - * Author: nicolas bellec - */ - -#ifndef PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_TYPEGRAPH_H_ -#define PHASAR_PHASARLLVM_POINTER_TYPEGRAPHS_TYPEGRAPH_H_ - -#include -#include -#include - -namespace llvm { -class StructType; -} // namespace llvm - -namespace psr { -template class TypeGraph { -public: - TypeGraph() = default; - - virtual ~TypeGraph() = default; - // TypeGraph(const TypeGraph ©) = delete; - // TypeGraph& operator=(const TypeGraph ©) = delete; - // TypeGraph(TypeGraph &&move) = delete; - // TypeGraph& operator=(TypeGraph &&move) = delete; - - /* Add a link if not already in the graph - * Return true if the node has been added, false otherwise - */ - virtual bool addLink(const llvm::StructType *From, - const llvm::StructType *To) = 0; - virtual void printAsDot(const std::string &Path = "typegraph.dot") const = 0; - virtual std::set - getTypes(const llvm::StructType *ST) = 0; -}; -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h index 53b93d9f1a..1b5694c6d4 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h @@ -14,11 +14,9 @@ #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/TypeHierarchy/TypeHierarchy.h" -#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" -#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/Support/Casting.h" @@ -101,10 +99,6 @@ class DIBasedTypeHierarchy */ void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const override; - /** * @brief Prints the class hierarchy to an ostream in json format. * @param an outputstream diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h index 3678dfa44c..1e1a79cfe6 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h @@ -51,7 +51,7 @@ class LLVMProjectIRDB; * hierarchy graph based on the data from the %ProjectIRCompiledDB * and reconstructing the virtual method tables. */ -class LLVMTypeHierarchy +class [[deprecated("Use DIBasedTypeHierarchy instead")]] LLVMTypeHierarchy : public TypeHierarchy { public: struct VertexProperties { @@ -110,11 +110,11 @@ class LLVMTypeHierarchy // map from clearname to vtable variable std::unordered_map ClearNameTVMap; - std::vector - getSubTypes(const llvm::Module &M, const llvm::StructType &Type) const; + std::vector getSubTypes( + const llvm::Module &M, const llvm::StructType &Type) const; - std::vector - getVirtualFunctions(const llvm::Module &M, const llvm::StructType &Type); + std::vector getVirtualFunctions( + const llvm::Module &M, const llvm::StructType &Type); protected: void buildLLVMTypeHierarchy(const llvm::Module &M); @@ -157,29 +157,29 @@ class LLVMTypeHierarchy */ void constructHierarchy(const llvm::Module &M); - [[nodiscard]] inline bool - hasType(const llvm::StructType *Type) const override { + [[nodiscard]] inline bool hasType(const llvm::StructType *Type) + const override { return TypeVertexMap.count(Type); } - [[nodiscard]] inline bool - isSubType(const llvm::StructType *Type, - const llvm::StructType *SubType) const override { + [[nodiscard]] inline bool isSubType(const llvm::StructType *Type, + const llvm::StructType *SubType) + const override { auto ReachableTypes = getSubTypes(Type); return ReachableTypes.count(SubType); } - std::set - getSubTypes(const llvm::StructType *Type) const override; + std::set getSubTypes(const llvm::StructType *Type) + const override; - [[nodiscard]] const llvm::StructType * - getType(llvm::StringRef TypeName) const override; + [[nodiscard]] const llvm::StructType *getType(llvm::StringRef TypeName) + const override; - [[nodiscard]] std::vector - getAllTypes() const override; + [[nodiscard]] std::vector getAllTypes() + const override; - [[nodiscard]] llvm::StringRef - getTypeName(const llvm::StructType *Type) const override; + [[nodiscard]] llvm::StringRef getTypeName(const llvm::StructType *Type) + const override; [[nodiscard]] size_t size() const noexcept override { return boost::num_vertices(TypeGraph); @@ -191,10 +191,6 @@ class LLVMTypeHierarchy void print(llvm::raw_ostream &OS = llvm::outs()) const override; - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const override; - // void mergeWith(LLVMTypeHierarchy &Other); /** diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h index cd2e226920..a68c1b341a 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h @@ -13,8 +13,6 @@ #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" #include "phasar/TypeHierarchy/VFTable.h" -#include "nlohmann/json.hpp" - #include namespace llvm { @@ -71,10 +69,6 @@ class LLVMVFTable : public VFTable { void print(llvm::raw_ostream &OS) const override; - [[nodiscard]] [[deprecated( - "Please use printAsJson() instead")]] nlohmann::json - getAsJson() const override; - [[nodiscard]] LLVMVFTableData getVFTableData() const; void printAsJson(llvm::raw_ostream &OS) const override; diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index 056969fe5e..db861acdca 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -139,12 +139,6 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { VT->Print(AA, OS); } - [[nodiscard, deprecated("Use printAsJson() instead")]] nlohmann::json - getAsJson() const { - assert(VT != nullptr); - return VT->GetAsJson(AA); - } - void printAsJson(llvm::raw_ostream &OS) const { assert(VT != nullptr); VT->PrintAsJson(AA, OS); @@ -200,7 +194,6 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { ByConstRef, bool, ByConstRef); void (*Print)(const void *, llvm::raw_ostream &); - nlohmann::json (*GetAsJson)(const void *); void (*PrintAsJson)(const void *, llvm::raw_ostream &); void (*MergeWith)(void *, void *); void (*IntroduceAlias)(void *, ByConstRef, ByConstRef, @@ -240,15 +233,6 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->print(OS); }, - [](const void *AA) noexcept { -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wdeprecated" - if constexpr (has_getAsJson::value) { - return static_cast(AA)->getAsJson(); - } - return nlohmann::json(); -#pragma GCC diagnostic pop - }, [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->printAsJson(OS); }, diff --git a/include/phasar/TypeHierarchy/TypeHierarchy.h b/include/phasar/TypeHierarchy/TypeHierarchy.h index d753652cf9..5d06705d1b 100644 --- a/include/phasar/TypeHierarchy/TypeHierarchy.h +++ b/include/phasar/TypeHierarchy/TypeHierarchy.h @@ -14,8 +14,6 @@ #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json_fwd.hpp" - #include namespace psr { @@ -40,10 +38,6 @@ template class TypeHierarchy { virtual void print(llvm::raw_ostream &OS = llvm::outs()) const = 0; - [[nodiscard, - deprecated("Please use printAsJson() instead")]] virtual nlohmann::json - getAsJson() const = 0; - virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index 83c544dbcd..a236f2e345 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -12,8 +12,6 @@ #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include namespace llvm { @@ -38,10 +36,6 @@ template class VFTable { virtual void print(llvm::raw_ostream &OS) const = 0; - [[nodiscard, - deprecated("Please use printAsJson() instead")]] virtual nlohmann::json - getAsJson() const = 0; - virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; diff --git a/include/phasar/Utils/Logger.h b/include/phasar/Utils/Logger.h index 6b3ea5e186..abf1fd65e6 100644 --- a/include/phasar/Utils/Logger.h +++ b/include/phasar/Utils/Logger.h @@ -155,12 +155,6 @@ class Logger final { #define PHASAR_LOG_LEVEL(level, message) (void)0 #endif -/** - * Initializes the logger. - */ -[[deprecated("Please use the new initialize*Logger() family instead")]] void -initializeLogger(bool UseLogger, const std::string &LogFile = ""); - } // namespace psr #endif diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index de2698c00e..f2c7504c74 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -33,16 +33,6 @@ std::string createTimeStamp(); bool isConstructor(llvm::StringRef MangledName); -namespace legacy { -// May need to call this function from a safe environment where we have already -// checked that it does not take any harm. Surround it with the legacy namespace -// as a marker that this function will be removed soon. - -/// [[deprecated("Requires non-opaque pointers, which will no longer be " -/// "supported by LLVM in the next version!")]] -const llvm::Type *stripPointer(const llvm::Type *Pointer); -} // namespace legacy - bool isMangled(llvm::StringRef Name); template diff --git a/lib/Config/Configuration.cpp b/lib/Config/Configuration.cpp index 11a36279e3..8378bacaab 100644 --- a/lib/Config/Configuration.cpp +++ b/lib/Config/Configuration.cpp @@ -55,10 +55,6 @@ llvm::StringRef PhasarConfig::PhasarDirectory() noexcept { return PHASAR_SRC_DIR; } -llvm::StringRef PhasarConfig::DefaultSourceSinkFunctionsPath() noexcept { - return PHASAR_SRC_DIR "/config/phasar-source-sink-function.json"; -} - static bool loadConfigFileInto(PhasarConfig &PC, llvm::StringRef FileName, std::set &Lines) { auto ConfigFile = PC.readConfigFileAsTextOrErr(FileName); diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp index 53a3a3adb2..1d9e41cf29 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp @@ -68,10 +68,6 @@ void LLVMBasedBackwardICFG::printAsJsonImpl(llvm::raw_ostream &OS) const { ForwardICFG->printAsJson(OS); } -nlohmann::json LLVMBasedBackwardICFG::getAsJsonImpl() const { - return ForwardICFG->getAsJson(); -} - auto LLVMBasedBackwardICFG::getCallGraphImpl() const noexcept -> const CallGraph & { return ForwardICFG->getCallGraph(); diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp index ce87316f5d..addd88c957 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp @@ -158,12 +158,6 @@ void LLVMBasedICFG::printAsJsonImpl(llvm::raw_ostream &OS) const { [this](n_t Inst) { return IRDB->getInstructionId(Inst); }); } -nlohmann::json LLVMBasedICFG::getAsJsonImpl() const { - return CG.getAsJson( - [](f_t F) { return F->getName().str(); }, - [this](n_t Inst) { return IRDB->getInstructionId(Inst); }); -} - template class ICFGBase; } // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/LLVMVFTableProvider.cpp b/lib/PhasarLLVM/ControlFlow/LLVMVFTableProvider.cpp index 43c6dab56d..45820e2793 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMVFTableProvider.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMVFTableProvider.cpp @@ -10,6 +10,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Module.h" #include "llvm/Support/Casting.h" diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/DTAResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/DTAResolver.cpp deleted file mode 100644 index 7863c024a4..0000000000 --- a/lib/PhasarLLVM/ControlFlow/Resolver/DTAResolver.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * DTAResolver.cpp - * - * Created on: 20.07.2018 - * Author: nicolas bellec - */ - -#include "phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h" - -#include "phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h" -#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" -#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/IR/Constants.h" -#include "llvm/IR/DebugInfoMetadata.h" -#include "llvm/IR/DerivedTypes.h" -#include "llvm/IR/Function.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Operator.h" -#include "llvm/IR/Value.h" -#include "llvm/Support/Casting.h" -#include "llvm/Support/ErrorHandling.h" - -#include - -using namespace psr; - -DTAResolver::DTAResolver(const LLVMProjectIRDB *IRDB, - const LLVMVFTableProvider *VTP, - const DIBasedTypeHierarchy *TH) - : CHAResolver(IRDB, VTP, TH) {} - -bool DTAResolver::heuristicAntiConstructorThisType( - const llvm::BitCastInst *BitCast) { - llvm::report_fatal_error("Does not work with opaque pointers anymore"); -#if 0 - // We check if the caller is a constructor, and if the this argument has the - // same type as the source type of the bitcast. If it is the case, it returns - // false, true otherwise. - - if (const auto *Caller = BitCast->getFunction()) { - if (isConstructor(Caller->getName().str())) { - if (auto *FuncTy = Caller->getFunctionType()) { - if (auto *ThisTy = FuncTy->getParamType(0)) { - if (ThisTy == BitCast->getSrcTy()) { - return false; - } - } - } - } - } - - return true; -#endif -} - -bool DTAResolver::heuristicAntiConstructorVtablePos( - const llvm::BitCastInst *BitCast) { - llvm::report_fatal_error("Does not work with opaque pointers anymore"); -#if 0 - - // Better heuristic than the previous one, can handle the CRTP. Based on the - // previous one. - - - if (heuristicAntiConstructorThisType(BitCast)) { - return true; - } - - // We know that we are in a constructor and the source type of the bitcast is - // the same as the this argument. We then check where the bitcast is against - // the store instruction of the vtable. - if (!BitCast->getSrcTy()->isOpaquePointerTy()) { - const auto *StructTy = psr::legacy::stripPointer(BitCast->getSrcTy()); - if (StructTy == nullptr) { - throw std::runtime_error( - "StructTy == nullptr in the heuristic_anti_contructor"); - } - - // If it doesn't contain vtable, there is no reason to call this class in - // the DTA graph, so no need to add it - if (StructTy->isStructTy()) { - if (VTP->hasVFTable(llvm::dyn_cast(StructTy))) { - return false; - } - } - } - // So there is a vtable, the question is, where is it compared to the bitcast - // instruction Carefull, there can be multiple vtable storage, we want to get - // the last one vtable storage typically are : store i32 (...)** bitcast (i8** - // getelementptr inbounds ({ [3 x i8*], [3 x i8*] }, { [3 x i8*], [3 x i8*] }* - // @_ZTV2AA, i32 0, inrange i32 1, i32 3) to i32 (...)**), i32 (...)*** %17, - // align 8 - // WARNING: May break when changing llvm version or using clang with version - // > 5.0.1 - - const auto *Caller = BitCast->getFunction(); - if (Caller == nullptr) { - throw std::runtime_error( - "A bitcast instruction has no associated function"); - } - - int Idx = 0; - - int VtableNum = 0; - - int BitcastNum = 0; - - for (auto I = llvm::inst_begin(Caller), E = llvm::inst_end(Caller); I != E; - ++I, ++Idx) { - const auto &Inst = *I; - - if (const auto *Store = llvm::dyn_cast(&Inst)) { - // We got a store instruction, now we are checking if it is a vtable - // storage - if (const auto *BitcastExpr = - llvm::dyn_cast(Store->getValueOperand())) { - if (BitcastExpr->isCast()) { - if (auto *ConstGep = llvm::dyn_cast( - BitcastExpr->getOperand(0))) { - if (auto *Gep = llvm::dyn_cast(ConstGep)) { - if (auto *Vtable = llvm::dyn_cast( - Gep->getPointerOperand())) { - // We can here assume that we found a vtable - VtableNum = Idx; - } - } - } - } - } - } - - if (&Inst == BitCast) { - BitcastNum = Idx; - } - } - - return (BitcastNum > VtableNum); -#endif -} - -void DTAResolver::otherInst(const llvm::Instruction *Inst) { - llvm::report_fatal_error("Does not work with opaque pointers anymore"); -#if 0 - if (Inst->getType()->isOpaquePointerTy()) { - /// XXX: We may want to get these information on a different way, e.g. by - /// analyzing the debug info - return; - } - if (const auto *BitCast = llvm::dyn_cast(Inst)) { - // We add the connection between the two types in the DTA graph - auto *Src = BitCast->getSrcTy(); - auto *Dest = BitCast->getDestTy(); - - const auto *SrcStructType = llvm::dyn_cast( - psr::legacy::stripPointer(Src)); // NOLINT - const auto *DestStructType = llvm::dyn_cast( - psr::legacy::stripPointer(Dest)); // NOLINT - - if (SrcStructType && DestStructType && - heuristicAntiConstructorVtablePos(BitCast)) { - TypeGraph.addLink(DestStructType, SrcStructType); - } - } -#endif -} -auto DTAResolver::resolveVirtualCall(const llvm::CallBase *CallSite) - -> FunctionSetTy { - llvm::report_fatal_error("Does not work with opaque pointers anymore"); -#if 0 - FunctionSetTy PossibleCallTargets; - - PHASAR_LOG_LEVEL(DEBUG, - "Call virtual function: " << llvmIRToString(CallSite)); - - auto RetrievedVtableIndex = getVFTIndex(CallSite); - if (!RetrievedVtableIndex.has_value()) { - // An error occured - PHASAR_LOG_LEVEL(DEBUG, - "Error with resolveVirtualCall : impossible to retrieve " - "the vtable index\n" - << llvmIRToString(CallSite) << "\n"); - return {}; - } - - auto VtableIndex = RetrievedVtableIndex.value(); - - PHASAR_LOG_LEVEL(DEBUG, "Virtual function table entry is: " << VtableIndex); - - const auto *ReceiverType = getReceiverStructType(CallSite); - - auto PossibleTypes = TypeGraph.getTypes(ReceiverType); - - // WARNING We deactivated the check on allocated because it is - // unabled to get the types allocated in the used libraries - // auto allocated_types = IRDB.getAllocatedTypes(); - // auto end_it = allocated_types.end(); - for (const auto *PossibleType : PossibleTypes) { - if (const auto *PossibleTypeStruct = - llvm::dyn_cast(PossibleType)) { - // if ( allocated_types.find(possible_type_struct) != end_it ) { - const auto *Target = - getNonPureVirtualVFTEntry(PossibleTypeStruct, VtableIndex, CallSite); - if (Target) { - PossibleCallTargets.insert(Target); - } - } - } - - if (PossibleCallTargets.empty()) { - PossibleCallTargets = CHAResolver::resolveVirtualCall(CallSite); - } - - PHASAR_LOG_LEVEL(DEBUG, "Possible targets are:"); -#ifdef DYNAMIC_LOG - for (const auto *Entry : PossibleCallTargets) { - PHASAR_LOG_LEVEL(DEBUG, Entry); - } -#endif - - return PossibleCallTargets; -#endif -} - -std::string DTAResolver::str() const { return "DTA"; } diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp index 27c91bcd4a..9065a43415 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp @@ -19,7 +19,6 @@ #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h" -#include "phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h" @@ -205,12 +204,6 @@ std::unique_ptr Resolver::create(CallGraphAnalysisType Ty, case CallGraphAnalysisType::RTA: assert(TH != nullptr); return std::make_unique(IRDB, VTP, TH); - case CallGraphAnalysisType::DTA: - assert(TH != nullptr); - PHASAR_LOG_LEVEL(ERROR, "Do not use the DTA resolver anymore. It relies on " - "the removed 'typed-pointers' feature of LLVM."); - std::exit(1); - // return std::make_unique(IRDB, VTP, TH); case CallGraphAnalysisType::VTA: llvm::report_fatal_error( "The VTA callgraph algorithm is not implemented yet"); diff --git a/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.cpp b/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.cpp index a61b147cc7..ac17cfbfe1 100644 --- a/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.cpp +++ b/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.cpp @@ -49,10 +49,6 @@ void SparseLLVMBasedICFGView::printImpl(llvm::raw_ostream &OS) const { ICF->print(OS); } -nlohmann::json SparseLLVMBasedICFGView::getAsJsonImpl() const { - return ICF->getAsJson(); -} - auto SparseLLVMBasedICFGView::getCallGraphImpl() const noexcept -> const CallGraph & { return ICF->getCallGraph(); diff --git a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp index ed64e47840..4575fa59ea 100644 --- a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp +++ b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp @@ -23,7 +23,6 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" @@ -252,43 +251,6 @@ GeneralStatistics GeneralStatisticsAnalysis::runOnModule(llvm::Module &M) { return Stats; } -size_t GeneralStatistics::getAllocationsites() const { return AllocationSites; } - -size_t GeneralStatistics::getFunctioncalls() const { return CallSites; } - -size_t GeneralStatistics::getInstructions() const { return Instructions; } - -size_t GeneralStatistics::getGlobalPointers() const { return Globals; } - -size_t GeneralStatistics::getBasicBlocks() const { return BasicBlocks; } - -size_t GeneralStatistics::getFunctions() const { return Functions; } - -size_t GeneralStatistics::getGlobals() const { return Globals; } - -size_t GeneralStatistics::getGlobalConsts() const { return GlobalConsts; } - -size_t GeneralStatistics::getMemoryIntrinsics() const { return MemIntrinsics; } - -size_t GeneralStatistics::getStoreInstructions() const { - return StoreInstructions; -} - -const std::set & -GeneralStatistics::getAllocatedTypes() const { - return AllocatedTypes; -} - -const std::set & -GeneralStatistics::getAllocaInstructions() const { - return AllocaInstructions; -} - -const std::set & -GeneralStatistics::getRetResInstructions() const { - return RetResInstructions; -} - void GeneralStatistics::printAsJson(llvm::raw_ostream &OS) const { nlohmann::json Json; @@ -333,14 +295,6 @@ void GeneralStatistics::printAsJson(llvm::raw_ostream &OS) const { OS << Json << '\n'; } -nlohmann::json GeneralStatistics::getAsJson() const { - std::string GeneralStatisticsAsString; - llvm::raw_string_ostream Stream(GeneralStatisticsAsString); - printAsJson(Stream); - - return nlohmann::json::parse(GeneralStatisticsAsString); -} - } // namespace psr namespace { diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasGraph.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasGraph.cpp deleted file mode 100644 index 26a0acac8f..0000000000 --- a/lib/PhasarLLVM/Pointer/LLVMAliasGraph.cpp +++ /dev/null @@ -1,469 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -#include "phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h" - -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" -#include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" -#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/NlohmannLogging.h" -#include "phasar/Utils/PAMMMacros.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/ADT/SetVector.h" -#include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/InstIterator.h" -#include "llvm/IR/Instructions.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Value.h" -#include "llvm/Support/raw_ostream.h" - -#include "boost/graph/copy.hpp" -#include "boost/graph/depth_first_search.hpp" -#include "boost/graph/graph_utility.hpp" -#include "boost/graph/graphviz.hpp" - -using namespace std; - -namespace psr { -struct LLVMAliasGraph::AllocationSiteDFSVisitor : boost::default_dfs_visitor { - // collect the allocation sites that are found - AliasSetTy &AllocationSites; - // keeps track of the current path - std::vector VisitorStack; - // the call stack that can be matched against the visitor stack - const std::vector &CallStack; - - AllocationSiteDFSVisitor(AliasSetTy &AllocationSizes, - const vector &CallStack) - : AllocationSites(AllocationSizes), CallStack(CallStack) {} - - template - void discover_vertex(Vertex U, const Graph & /*G*/) { - VisitorStack.push_back(U); - } - - template - void finish_vertex(Vertex U, const Graph &G) { - // check for stack allocation - if (const auto *Alloc = llvm::dyn_cast(G[U].V)) { - // If the call stack is empty, we completely ignore the calling context - if (matchesStack(G) || CallStack.empty()) { - PHASAR_LOG_LEVEL(DEBUG, - "Found stack allocation: " << llvmIRToString(Alloc)); - AllocationSites.insert(G[U].V); - } - } - // check for heap allocation - if (llvm::isa(G[U].V) || - llvm::isa(G[U].V)) { - const auto *CallSite = llvm::cast(G[U].V); - if (CallSite->getCalledFunction() != nullptr && - isHeapAllocatingFunction(CallSite->getCalledFunction())) { - // If the call stack is empty, we completely ignore the calling - // context - if (matchesStack(G) || CallStack.empty()) { - PHASAR_LOG_LEVEL( - DEBUG, "Found heap allocation: " << llvmIRToString(CallSite)); - AllocationSites.insert(G[U].V); - } - } - } - VisitorStack.pop_back(); - } - - template bool matchesStack(const Graph &G) { - size_t CallStackIdx = 0; - for (size_t I = 0, J = 1; - I < VisitorStack.size() && J < VisitorStack.size(); ++I, ++J) { - auto E = boost::edge(VisitorStack[I], VisitorStack[J], G); - if (G[E.first].V == nullptr) { - continue; - } - if (G[E.first].V != CallStack[CallStack.size() - CallStackIdx - 1]) { - return false; - } - CallStackIdx++; - } - return true; - } -}; - -struct LLVMAliasGraph::ReachabilityDFSVisitor : boost::default_dfs_visitor { - std::set &AliasSet; - ReachabilityDFSVisitor(set &Result) : AliasSet(Result) {} - template - void finish_vertex(Vertex U, const Graph & /*Graph*/) { - AliasSet.insert(U); - } -}; - -// points-to graph internal stuff - -LLVMAliasGraph::VertexProperties::VertexProperties(const llvm::Value *V) - : V(V) {} - -std::string LLVMAliasGraph::VertexProperties::getValueAsString() const { - return llvmIRToString(V); -} - -std::vector -LLVMAliasGraph::VertexProperties::getUsers() const { - if (!Users.empty() || V == nullptr) { - return Users; - } - auto AllUsers = V->users(); - Users.insert(Users.end(), AllUsers.begin(), AllUsers.end()); - return Users; -} - -LLVMAliasGraph::EdgeProperties::EdgeProperties(const llvm::Value *V) : V(V) {} - -std::string LLVMAliasGraph::EdgeProperties::getValueAsString() const { - return llvmIRToString(V); -} - -// points-to graph stuff - -LLVMAliasGraph::LLVMAliasGraph(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, - AliasAnalysisType /*PATy*/) - : PTA(IRDB, UseLazyEvaluation) {} - -void LLVMAliasGraph::computeAliasGraph(const llvm::Value *V) { - // FIXME when fixed in LLVM - auto *VF = const_cast(retrieveFunction(V)); // NOLINT - computeAliasGraph(VF); -} - -void LLVMAliasGraph::computeAliasGraph(llvm::Function *F) { - // check if we already analyzed the function - if (AnalyzedFunctions.find(F) != AnalyzedFunctions.end()) { - return; - } - PAMM_GET_INSTANCE; - PHASAR_LOG_LEVEL(DEBUG, "Analyzing function: " << F->getName()); - AnalyzedFunctions.insert(F); - llvm::AAResults &AA = *PTA.getAAResults(F); - bool EvalAAMD = true; - - // taken from llvm/Analysis/AliasAnalysisEvaluator.cpp - const llvm::DataLayout &DL = F->getParent()->getDataLayout(); - - llvm::SetVector Pointers; - llvm::SmallSetVector Calls; - llvm::SetVector Loads; - llvm::SetVector Stores; - - for (auto &I : F->args()) { - if (I.getType()->isPointerTy()) { // Add all pointer arguments. - Pointers.insert(&I); - } - } - - for (llvm::inst_iterator I = inst_begin(*F), E = inst_end(*F); I != E; ++I) { - if (I->getType()->isPointerTy()) { // Add all pointer instructions. - Pointers.insert(&*I); - } - if (EvalAAMD && llvm::isa(&*I)) { - Loads.insert(&*I); - } - if (EvalAAMD && llvm::isa(&*I)) { - Stores.insert(&*I); - } - llvm::Instruction &Inst = *I; - if (auto *Call = llvm::dyn_cast(&Inst)) { - llvm::Value *Callee = Call->getCalledOperand(); - // Skip actual functions for direct function calls. - if (!llvm::isa(Callee) && isInterestingPointer(Callee)) { - Pointers.insert(Callee); - } - // Consider formals. - for (llvm::Use &DataOp : Call->data_ops()) { - if (isInterestingPointer(DataOp)) { - Pointers.insert(DataOp); - } - } - Calls.insert(Call); - } else { - // Consider all operands. - for (llvm::Instruction::op_iterator OI = Inst.op_begin(), - OE = Inst.op_end(); - OI != OE; ++OI) { - if (isInterestingPointer(*OI)) { - Pointers.insert(*OI); - } - } - } - } - - INC_COUNTER("GS Pointer", Pointers.size(), Core); - - // make vertices for all pointers - for (auto *P : Pointers) { - ValueVertexMap[P] = boost::add_vertex(VertexProperties(P), PAG); - } - // iterate over the worklist, and run the full (n^2)/2 disambiguations - const auto MapEnd = ValueVertexMap.end(); - for (auto I1 = ValueVertexMap.begin(); I1 != MapEnd; ++I1) { - llvm::Type *I1ElTy = - !I1->first->getType()->isOpaquePointerTy() - ? I1->first->getType()->getNonOpaquePointerElementType() - : nullptr; - const uint64_t I1Size = I1ElTy && I1ElTy->isSized() - ? DL.getTypeStoreSize(I1ElTy) - : llvm::MemoryLocation::UnknownSize; - for (auto I2 = std::next(I1); I2 != MapEnd; ++I2) { - llvm::Type *I2ElTy = - !I2->first->getType()->isOpaquePointerTy() - ? I2->first->getType()->getNonOpaquePointerElementType() - : nullptr; - const uint64_t I2Size = I2ElTy && I2ElTy->isSized() - ? DL.getTypeStoreSize(I2ElTy) - : llvm::MemoryLocation::UnknownSize; - switch (AA.alias(I1->first, I1Size, I2->first, I2Size)) { - case llvm::AliasResult::NoAlias: - break; - case llvm::AliasResult::MayAlias: // no break - [[fallthrough]]; - case llvm::AliasResult::PartialAlias: // no break - [[fallthrough]]; - case llvm::AliasResult::MustAlias: - boost::add_edge(I1->second, I2->second, PAG); - break; - default: - break; - } - } - } -} - -bool LLVMAliasGraph::isInterProcedural() const noexcept { return false; } - -AliasAnalysisType LLVMAliasGraph::getAliasAnalysisType() const noexcept { - return AliasAnalysisType::Invalid; -} - -AliasResult LLVMAliasGraph::alias(const llvm::Value *V1, const llvm::Value *V2, - const llvm::Instruction * /*I*/) { - computeAliasGraph(V1); - computeAliasGraph(V2); - auto PTS = getAliasSet(V1); - if (PTS->contains(V2)) { - return AliasResult::MustAlias; - } - return AliasResult::NoAlias; -} - -auto LLVMAliasGraph::getReachableAllocationSites( - const llvm::Value *V, bool /*IntraProcOnly*/, - const llvm::Instruction * /*I*/) -> AllocationSiteSetPtrTy { - computeAliasGraph(V); - auto AllocSites = std::make_unique(); - AllocationSiteDFSVisitor AllocVis(*AllocSites, {}); - vector ColorMap(boost::num_vertices(PAG)); - boost::depth_first_visit( - PAG, ValueVertexMap[V], AllocVis, - boost::make_iterator_property_map( - ColorMap.begin(), boost::get(boost::vertex_index, PAG), ColorMap[0])); - return AllocSites; -} - -[[nodiscard]] bool LLVMAliasGraph::isInReachableAllocationSites( - const llvm::Value *V, const llvm::Value *PotentialValue, bool IntraProcOnly, - const llvm::Instruction *I) { - return getReachableAllocationSites(V, IntraProcOnly, I) - ->count(PotentialValue); -} - -void LLVMAliasGraph::mergeWith(const LLVMAliasGraph &OtherPTI) { - AnalyzedFunctions.insert(OtherPTI.AnalyzedFunctions.begin(), - OtherPTI.AnalyzedFunctions.end()); - using vertex_t = graph_t::vertex_descriptor; - using vertex_map_t = std::map; - vertex_map_t OldToNewVertexMapping; - boost::associative_property_map VertexMapWrapper( - OldToNewVertexMapping); - boost::copy_graph(OtherPTI.PAG, PAG, boost::orig_to_copy(VertexMapWrapper)); - for (const auto &OtherValues : OtherPTI.ValueVertexMap) { - auto Search = OldToNewVertexMapping.find(OtherValues.second); - if (Search != OldToNewVertexMapping.end()) { - ValueVertexMap.insert(make_pair(OtherValues.first, Search->second)); - } - } -} - -void LLVMAliasGraph::introduceAlias(const llvm::Value *V1, - const llvm::Value *V2, - const llvm::Instruction *I, - AliasResult /*Kind*/) { - computeAliasGraph(V1); - computeAliasGraph(V2); - auto Vert1 = ValueVertexMap[V1]; - auto Vert2 = ValueVertexMap[V2]; - boost::add_edge(Vert1, Vert2, I, PAG); -} - -vector> -LLVMAliasGraph::getPointersEscapingThroughParams() { - vector> EscapingPointers; - for (auto VertexIter : boost::make_iterator_range(boost::vertices(PAG))) { - if (const auto *Arg = llvm::dyn_cast(PAG[VertexIter].V)) { - EscapingPointers.emplace_back(Arg->getArgNo(), Arg); - } - } - return EscapingPointers; -} - -vector -LLVMAliasGraph::getPointersEscapingThroughReturns() const { - vector EscapingPointers; - for (auto VertexIter : boost::make_iterator_range(boost::vertices(PAG))) { - const auto &Vertex = PAG[VertexIter]; - for (const auto *const User : Vertex.getUsers()) { - if (llvm::isa(User)) { - EscapingPointers.push_back(Vertex.V); - } - } - } - return EscapingPointers; -} - -vector -LLVMAliasGraph::getPointersEscapingThroughReturnsForFunction( - const llvm::Function *F) const { - vector EscapingPointers; - for (auto VertexIter : boost::make_iterator_range(boost::vertices(PAG))) { - const auto &Vertex = PAG[VertexIter]; - for (const auto *const User : Vertex.getUsers()) { - if (const auto *R = llvm::dyn_cast(User)) { - if (R->getFunction() == F) { - EscapingPointers.push_back(Vertex.V); - } - } - } - } - return EscapingPointers; -} - -bool LLVMAliasGraph::containsValue(llvm::Value *V) { - for (auto VertexIter : boost::make_iterator_range(boost::vertices(PAG))) { - if (PAG[VertexIter].V == V) { - return true; - } - } - return false; -} - -auto LLVMAliasGraph::getAliasSet(const llvm::Value *V, - const llvm::Instruction * /*I*/) - -> AliasSetPtrTy { - PAMM_GET_INSTANCE; - INC_COUNTER("[Calls] getAliasSet", 1, Full); - START_TIMER("Alias-Set Computation", Full); - const auto *VF = retrieveFunction(V); - computeAliasGraph(VF); - // check if the graph contains a corresponding vertex - set ReachableVertices; - ReachabilityDFSVisitor Vis(ReachableVertices); - vector ColorMap(boost::num_vertices(PAG)); - boost::depth_first_visit( - PAG, ValueVertexMap.at(V), Vis, - boost::make_iterator_property_map( - ColorMap.begin(), boost::get(boost::vertex_index, PAG), ColorMap[0])); - auto ResultSet = [this, V] { - auto &Ret = Cache[V]; - - if (!Ret) { - Ret = Owner.acquire(); - } - - return Ret; - }(); - - for (auto Vertex : ReachableVertices) { - ResultSet->insert(PAG[Vertex].V); - } - PAUSE_TIMER("Alias-Set Computation", Full); - ADD_TO_HISTOGRAM("Points-to", ResultSet->size(), 1, Full); - return ResultSet; -} - -void LLVMAliasGraph::print(llvm::raw_ostream &OS) const { - for (const auto &Fn : AnalyzedFunctions) { - llvm::outs() << "LLVMAliasGraph for " << Fn->getName() << ":\n"; - vertex_iterator UI; - - vertex_iterator UIEnd; - for (boost::tie(UI, UIEnd) = boost::vertices(PAG); UI != UIEnd; ++UI) { - OS << PAG[*UI].getValueAsString() << " <--> "; - out_edge_iterator EI; - - out_edge_iterator EIEnd; - for (boost::tie(EI, EIEnd) = boost::out_edges(*UI, PAG); EI != EIEnd; - ++EI) { - OS << PAG[target(*EI, PAG)].getValueAsString() << " "; - } - OS << '\n'; - } - } -} - -void LLVMAliasGraph::printAsDot(llvm::raw_ostream &OS) const { - std::stringstream S; - boost::write_graphviz(S, PAG, makePointerVertexOrEdgePrinter(PAG), - makePointerVertexOrEdgePrinter(PAG)); - OS << S.str(); -} - -nlohmann::json LLVMAliasGraph::getAsJson() const { - nlohmann::json J; - vertex_iterator VIv; - - vertex_iterator VIvEnd; - out_edge_iterator EI; - - out_edge_iterator EIEnd; - // iterate all graph vertices - for (boost::tie(VIv, VIvEnd) = boost::vertices(PAG); VIv != VIvEnd; ++VIv) { - J[PhasarConfig::JsonAliasGraphID().str()][PAG[*VIv].getValueAsString()]; - // iterate all out edges of vertex vi_v - for (boost::tie(EI, EIEnd) = boost::out_edges(*VIv, PAG); EI != EIEnd; - ++EI) { - J[PhasarConfig::JsonAliasGraphID().str()][PAG[*VIv].getValueAsString()] += - PAG[boost::target(*EI, PAG)].getValueAsString(); - } - } - return J; -} - -void LLVMAliasGraph::printValueVertexMap() { - for (const auto &Entry : ValueVertexMap) { - llvm::outs() << Entry.first << " <---> " << Entry.second << '\n'; - } -} - -bool LLVMAliasGraph::empty() const { return size() == 0; } - -size_t LLVMAliasGraph::size() const { return getNumVertices(); } - -size_t LLVMAliasGraph::getNumVertices() const { - return boost::num_vertices(PAG); -} - -size_t LLVMAliasGraph::getNumEdges() const { return boost::num_edges(PAG); } - -void LLVMAliasGraph::printAsJson(llvm::raw_ostream &OS) const { - nlohmann::json J = getAsJson(); - OS << J; -} - -} // namespace psr diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index ecc2eb532d..6beca389fb 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -716,33 +716,6 @@ void LLVMAliasSet::introduceAlias(const llvm::Value *V1, const llvm::Value *V2, mergeAliasSets(V1, V2); } -nlohmann::json LLVMAliasSet::getAsJson() const { - nlohmann::json J; - - /// Serialize the AliasSets - auto &Sets = J["AliasSets"]; - - for (const AliasSetTy *PTS : Owner.getAllAliasSets()) { - auto PtsJson = nlohmann::json::array(); - for (const auto *Alias : *PTS) { - auto Id = getMetaDataID(Alias); - if (Id != "-1") { - PtsJson.push_back(std::move(Id)); - } - } - if (!PtsJson.empty()) { - Sets.push_back(std::move(PtsJson)); - } - } - - /// Serialize the AnalyzedFunctions - auto &Fns = J["AnalyzedFunctions"]; - for (const auto *F : AnalyzedFunctions) { - Fns.push_back(F->getName()); - } - return J; -} - LLVMAliasSetData LLVMAliasSet::getLLVMAliasSetData() const { LLVMAliasSetData Data; diff --git a/lib/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.cpp b/lib/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.cpp deleted file mode 100644 index 77c7ab8698..0000000000 --- a/lib/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.cpp +++ /dev/null @@ -1,159 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * TypeGraph.cpp - * - * Created on: 28.06.2018 - * Author: nicolas bellec - */ - -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h" - -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/IR/DerivedTypes.h" - -#include "boost/graph/depth_first_search.hpp" -#include "boost/graph/graph_utility.hpp" -#include "boost/graph/graphviz.hpp" -#include "boost/property_map/dynamic_property_map.hpp" - -using namespace std; -using namespace psr; - -namespace psr { - -struct CachedTypeGraph::dfs_visitor : public boost::default_dfs_visitor { - dfs_visitor(graph_t *G) : G(G) {} - - void finish_edge(edge_t E, graph_t const &U) { // NOLINT - CachedTypeGraph::vertex_t Src = boost::source(E, U); - CachedTypeGraph::vertex_t Target = boost::target(E, U); - - for (const auto *TargetType : U[Target].Types) { - (*G)[Src].Types.insert(TargetType); - } - } - - graph_t *G; -}; - -struct CachedTypeGraph::reverse_type_propagation_dfs_visitor - : public boost::default_dfs_visitor { - reverse_type_propagation_dfs_visitor(rev_graph_t *G) : G(G) {} - - void examine_edge(rev_edge_t E, rev_graph_t const &U) { // NOLINT - auto Src = boost::source(E, U); - auto Target = boost::target(E, U); - - for (const auto *SrcType : U[Src].Types) { - (*G)[Target].Types.insert(SrcType); - } - } - - rev_graph_t *G; -}; - -CachedTypeGraph::vertex_t -CachedTypeGraph::addType(const llvm::StructType *NewType) { - std::string Name; - if (!NewType->isLiteral()) { - Name = NewType->getName(); - } else { - std::stringstream StrS; - StrS << "literal_" << NewType; - Name = StrS.str(); - } - - if (TypeVertexMap.find(Name) == TypeVertexMap.end()) { - auto Vertex = boost::add_vertex(G); - TypeVertexMap[Name] = Vertex; - G[Vertex].Name = Name; - G[Vertex].BaseType = NewType; - G[Vertex].Types.insert(NewType); - } - - return TypeVertexMap[Name]; -} - -bool CachedTypeGraph::addLink(const llvm::StructType *From, - const llvm::StructType *To) { - if (AlreadyVisited) { - return false; - } - - AlreadyVisited = true; - - auto FromVertex = addType(From); - auto ToVertex = addType(To); - - auto ResultEdge = boost::add_edge(FromVertex, ToVertex, G); - - if (ResultEdge.second) { - reverseTypePropagation(To); - } - - AlreadyVisited = false; - return ResultEdge.second; -} - -bool CachedTypeGraph::addLinkWithoutReversePropagation( - const llvm::StructType *From, const llvm::StructType *To) { - if (AlreadyVisited) { - return false; - } - - AlreadyVisited = true; - - auto FromVertex = addType(From); - auto ToVertex = addType(To); - - auto ResultEdge = boost::add_edge(FromVertex, ToVertex, G); - - AlreadyVisited = false; - return ResultEdge.second; -} - -void CachedTypeGraph::printAsDot(const std::string &Path) const { - std::ofstream Ofs(Path); - boost::write_graphviz( - Ofs, G, boost::make_label_writer(boost::get(&VertexProperties::Name, G))); -} - -void CachedTypeGraph::aggregateTypes() { - dfs_visitor Vis(&G); - boost::depth_first_search(G, boost::visitor(Vis)); -} - -void CachedTypeGraph::reverseTypePropagation( - const llvm::StructType *BaseStruct) { - auto Name = BaseStruct->getName().str(); - - std::vector ColorMap(boost::num_vertices(G)); - - auto Reversed = boost::reverse_graph(G); - reverse_type_propagation_dfs_visitor Vis(&Reversed); - - boost::depth_first_visit(Reversed, TypeVertexMap[Name], Vis, - boost::make_iterator_property_map( - ColorMap.begin(), - boost::get(boost::vertex_index, Reversed), - ColorMap[0])); -} - -std::set -CachedTypeGraph::getTypes(const llvm::StructType *StructType) { - auto StructTyVertex = addType(StructType); - return G[StructTyVertex].Types; -} - -} // namespace psr diff --git a/lib/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.cpp b/lib/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.cpp deleted file mode 100644 index ee33541287..0000000000 --- a/lib/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/****************************************************************************** - * Copyright (c) 2017 Philipp Schubert. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Philipp Schubert and others - *****************************************************************************/ - -/* - * LazyLazyTypeGraph.cpp - * - * Created on: 28.06.2018 - * Author: nicolas bellec - */ - -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" - -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/IR/DerivedTypes.h" - -#include "boost/graph/depth_first_search.hpp" -#include "boost/graph/graph_utility.hpp" -#include "boost/graph/graphviz.hpp" -#include "boost/property_map/dynamic_property_map.hpp" - -using namespace std; -using namespace psr; - -namespace psr { - -struct LazyTypeGraph::dfs_visitor : public boost::default_dfs_visitor { - dfs_visitor(std::set &Result) : Result(Result) {} - - void finish_edge(edge_t E, graph_t const &U) { - LazyTypeGraph::vertex_t Target = boost::target(E, U); - - Result.insert(U[Target].SType); - } - - std::set &Result; -}; - -LazyTypeGraph::vertex_t -LazyTypeGraph::addType(const llvm::StructType *NewType) { - auto Name = NewType->getName().str(); - - if (TypeToVertexMap.find(Name) == TypeToVertexMap.end()) { - auto Vertex = boost::add_vertex(Graph); - TypeToVertexMap[Name] = Vertex; - Graph[Vertex].Name = Name; - Graph[Vertex].SType = NewType; - } - - return TypeToVertexMap[Name]; -} - -bool LazyTypeGraph::addLink(const llvm::StructType *From, - const llvm::StructType *To) { - if (AlreadyVisited) { - return false; - } - - AlreadyVisited = true; - - auto FromVertex = addType(From); - auto ToVertex = addType(To); - - auto ResultEdge = boost::add_edge(FromVertex, ToVertex, Graph); - - AlreadyVisited = false; - return ResultEdge.second; -} - -void LazyTypeGraph::printAsDot(const std::string &Path) const { - std::ofstream Ofs(Path); - boost::write_graphviz(Ofs, Graph, - boost::make_label_writer(boost::get( - &LazyTypeGraph::VertexProperties::Name, Graph))); -} - -std::set -LazyTypeGraph::getTypes(const llvm::StructType *StructType) { - auto StructTyVertex = addType(StructType); - - std::vector ColorMap(boost::num_vertices(Graph)); - - std::set Results; - Results.insert(StructType); - - dfs_visitor Vis(Results); - - boost::depth_first_visit( - Graph, StructTyVertex, Vis, - boost::make_iterator_property_map(ColorMap.begin(), - boost::get(boost::vertex_index, Graph), - ColorMap[0])); - - return Results; -} - -} // namespace psr diff --git a/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp index d6188b1b53..36319dc05a 100644 --- a/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp @@ -361,12 +361,6 @@ void DIBasedTypeHierarchy::print(llvm::raw_ostream &OS) const { } } -[[nodiscard]] [[deprecated("Please use printAsJson() instead")]] nlohmann::json -DIBasedTypeHierarchy::getAsJson() const { - /// TODO: implement - llvm::report_fatal_error("Not implemented"); -} - DIBasedTypeHierarchyData DIBasedTypeHierarchy::getTypeHierarchyData() const { DIBasedTypeHierarchyData Data; diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp index 2e0707f9f4..028abe8659 100644 --- a/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp @@ -49,7 +49,7 @@ using namespace std; namespace psr { // provide a VertexPropertyWrite to tell boost how to write a vertex -class TypeHierarchyVertexWriter { +class [[deprecated]] TypeHierarchyVertexWriter { public: TypeHierarchyVertexWriter(const LLVMTypeHierarchy::bidigraph_t &TyGraph) : TyGraph(TyGraph) {} @@ -389,75 +389,6 @@ void LLVMTypeHierarchy::print(llvm::raw_ostream &OS) const { } } -nlohmann::json LLVMTypeHierarchy::getAsJson() const { - nlohmann::json J; - vertex_iterator VIv; - - vertex_iterator VIvEnd; - out_edge_iterator EI; - - out_edge_iterator EIEnd; - // iterate all graph vertices - for (boost::tie(VIv, VIvEnd) = boost::vertices(TypeGraph); VIv != VIvEnd; - ++VIv) { - J[PhasarConfig::JsonTypeHierarchyID().str()][TypeGraph[*VIv].getTypeName()]; - // iterate all out edges of vertex vi_v - for (boost::tie(EI, EIEnd) = boost::out_edges(*VIv, TypeGraph); EI != EIEnd; - ++EI) { - J[PhasarConfig::JsonTypeHierarchyID().str()] - [TypeGraph[*VIv].getTypeName()] += - TypeGraph[boost::target(*EI, TypeGraph)].getTypeName(); - } - } - return J; -} - -// void LLVMTypeHierarchy::mergeWith(LLVMTypeHierarchy &Other) { -// cout << "LLVMTypeHierarchy::mergeWith()" << endl; -// boost::copy_graph(Other.TypeGraph, TypeGraph); // G += H; -// // build the contractions -// vector> contractions; -// map observed; -// for (auto V : boost::make_iterator_range(boost::vertices(TypeGraph))) { -// if (observed.find(TypeGraph[V].name) != observed.end()) { -// // check which one has the valid pointer and the knowledge of the -// vtables if (TypeGraph[V].llvmtype) { -// contractions.push_back(make_pair(observed[TypeGraph[V].name], V)); -// } else { -// contractions.push_back(make_pair(V, observed[TypeGraph[V].name])); -// } -// } else { -// observed[TypeGraph[V].name] = V; -// } -// } -// cout << "contractions.size(): " << contractions.size() << '\n'; -// for (auto contraction : contractions) { -// contract_vertices( -// contraction.first, contraction.second, TypeGraph); -// } -// // merge the vtables -// TypeVFTMap.insert(Other.TypeVFTMap.begin(), -// Other.TypeVFTMap.end()); -// // merge the modules analyzed -// contained_modules.insert(Other.contained_modules.begin(), -// Other.contained_modules.end()); -// // reset the vertex mapping -// TypeVertexMap.clear(); -// for (auto V : boost::make_iterator_range(boost::vertices(TypeGraph))) { -// TypeVertexMap[TypeGraph[V].name] = V; -// } -// // cache the reachable types -// bidigraph_t tc; -// boost::transitive_closure(TypeGraph, tc); -// for (auto V : boost::make_iterator_range(boost::vertices(TypeGraph))) { -// for (auto OE : boost::make_iterator_range(boost::out_edges(V, tc))) { -// auto Source = boost::source(OE, tc); -// auto Target = boost::target(OE, tc); -// TypeGraph[V].reachableTypes.insert(TypeGraph[Target].name); -// } -// } -// } - void LLVMTypeHierarchy::printAsDot(llvm::raw_ostream &OS) const { std::stringstream S; boost::write_graphviz(S, TypeGraph, TypeHierarchyVertexWriter(TypeGraph)); diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp index b25822da84..f2a5a849bd 100644 --- a/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp @@ -48,11 +48,6 @@ void LLVMVFTable::print(llvm::raw_ostream &OS) const { } } -nlohmann::json LLVMVFTable::getAsJson() const { - nlohmann::json J = "{}"_json; - return J; -} - [[nodiscard]] LLVMVFTableData LLVMVFTable::getVFTableData() const { LLVMVFTableData Data; diff --git a/lib/Utils/Logger.cpp b/lib/Utils/Logger.cpp index 35cd18a29f..b166bf67b6 100644 --- a/lib/Utils/Logger.cpp +++ b/lib/Utils/Logger.cpp @@ -225,16 +225,3 @@ void Logger::addLinePrefix(llvm::raw_ostream &OS, } } // namespace psr - -void psr::initializeLogger(bool UseLogger, const std::string &LogFile) { - if (!UseLogger) { - Logger::disable(); - return; - } - if (LogFile.empty()) { - Logger::initializeStderrLogger(Logger::getLoggerFilterLevel()); - } else { - std::ignore = - Logger::initializeFileLogger(LogFile, Logger::getLoggerFilterLevel()); - } -} diff --git a/lib/Utils/Utilities.cpp b/lib/Utils/Utilities.cpp index 28e8d378ec..0c2aeedaca 100644 --- a/lib/Utils/Utilities.cpp +++ b/lib/Utils/Utilities.cpp @@ -64,19 +64,6 @@ bool isConstructor(llvm::StringRef MangledName) { return false; } -const llvm::Type *legacy::stripPointer(const llvm::Type *Pointer) { - const auto *Next = llvm::dyn_cast(Pointer); - while (Next) { - assert(!Next->isOpaquePointerTy() && - "Don't call stripPointer, when analyzing IR that uses opaque " - "pointers!"); - Pointer = Next->getNonOpaquePointerElementType(); - Next = llvm::dyn_cast(Pointer); - } - - return Pointer; -} - bool isMangled(llvm::StringRef Name) { // See llvm/Demangle/Demangle.cpp if (Name.startswith("_Z") || Name.startswith("___Z")) { diff --git a/unittests/DB/HexastoreTest.cpp b/unittests/DB/HexastoreTest.cpp index 06bc7ff83a..6ce20b23a7 100644 --- a/unittests/DB/HexastoreTest.cpp +++ b/unittests/DB/HexastoreTest.cpp @@ -100,12 +100,12 @@ TEST(HexastoreTest, StoreGraphNoEdgeLabels) { struct Vertex { string Name; Vertex() = default; - Vertex(string Name) : Name(move(Name)) {} + Vertex(string Name) : Name(std::move(Name)) {} }; struct Edge { string EdgeName; Edge() = default; - Edge(string Label) : EdgeName(move(Label)) {} + Edge(string Label) : EdgeName(std::move(Label)) {} }; using graph_t = boost::adjacency_list(I) || llvm::isa(I)) { - const auto &Callees = ICFG.getCalleesOfCallAt(I); - - ASSERT_TRUE(ICFG.isVirtualFunctionCall(I)); - ASSERT_EQ(Callees.size(), 2U); - ASSERT_TRUE(llvm::is_contained(Callees, VFuncB)); - ASSERT_TRUE(llvm::is_contained(Callees, VFuncA)); - ASSERT_TRUE(llvm::is_contained(ICFG.getCallersOf(VFuncA), I)); - ASSERT_TRUE(llvm::is_contained(ICFG.getCallersOf(VFuncB), I)); - } -} - -TEST(LLVMBasedICFG_DTATest, VirtualCallSite_6) { - GTEST_SKIP() << "Requires typed pointers!"; - LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + - "call_graphs/virtual_call_6_cpp.ll"); - DIBasedTypeHierarchy TH(IRDB); - LLVMAliasSet PT(&IRDB); - LLVMBasedICFG ICFG(&IRDB, CallGraphAnalysisType::DTA, {"main"}, &TH, &PT); - const llvm::Function *F = IRDB.getFunctionDefinition("main"); - const llvm::Function *VFuncA = IRDB.getFunctionDefinition("_ZN1A5VfuncEv"); - const llvm::Function *VFuncB = IRDB.getFunctionDefinition("_ZN1B5VfuncEv"); - ASSERT_TRUE(F); - ASSERT_TRUE(VFuncA); - ASSERT_TRUE(VFuncB); - - const llvm::Instruction *I = getNthInstruction(F, 6); - const auto &Callers = ICFG.getCallersOf(VFuncA); - ASSERT_EQ(Callers.size(), 1U); - ASSERT_TRUE(llvm::is_contained(Callers, I)); -} - -int main(int Argc, char **Argv) { - ::testing::InitGoogleTest(&Argc, Argv); - return RUN_ALL_TESTS(); -} diff --git a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt index d4407ecef7..fc190151ac 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt +++ b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt @@ -3,7 +3,6 @@ set(PointerSources DIBasedTypeHierarchyTest.cpp LLVMTypeHierarchySerializationTest.cpp LLVMTypeHierarchyTest.cpp - TypeGraphTest.cpp ) foreach(TEST_SRC ${PointerSources}) diff --git a/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp index 8ce287caf4..9042b52547 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp +++ b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp @@ -15,6 +15,9 @@ using namespace psr; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" + /* ============== TEST FIXTURE ============== */ class LLVMTypeHierarchySerialization : public ::testing::TestWithParam { @@ -65,6 +68,8 @@ TEST_P(LLVMTypeHierarchySerialization, OrigAndDeserEqual) { compareResults(TypeHierarchy, DeserializedTypeHierarchy); } +#pragma GCC diagnostic pop + static constexpr std::string_view TypeHierarchyTestFiles[] = { "type_hierarchy_1_cpp_dbg.ll", "type_hierarchy_2_cpp_dbg.ll", "type_hierarchy_3_cpp_dbg.ll", "type_hierarchy_4_cpp_dbg.ll", diff --git a/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyTest.cpp index 0d302c9124..c9404f6070 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyTest.cpp +++ b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyTest.cpp @@ -20,6 +20,9 @@ using namespace psr; using llvm::demangle; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" + namespace psr { // Check basic type hierarchy construction @@ -408,6 +411,8 @@ PHASAR_SKIP_TEST(TEST(LTHTest, HandleSTLString) { } // namespace psr +#pragma GCC diagnostic pop + int main(int Argc, char **Argv) { ::testing::InitGoogleTest(&Argc, Argv); auto Res = RUN_ALL_TESTS(); diff --git a/unittests/PhasarLLVM/TypeHierarchy/TypeGraphTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/TypeGraphTest.cpp deleted file mode 100644 index 22a5252239..0000000000 --- a/unittests/PhasarLLVM/TypeHierarchy/TypeGraphTest.cpp +++ /dev/null @@ -1,417 +0,0 @@ -#include "gtest/gtest.h" -// -- Need gtest for the FRIEND_TEST macro -#define PSR_FRIEND_TEST(TEST, CLASS) FRIEND_TEST(TEST, CLASS); - -#include "phasar/Config/Configuration.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h" -#include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" -#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/Support/ManagedStatic.h" - -#include "TestConfig.h" -#include "boost/graph/isomorphism.hpp" - -using namespace std; -using namespace psr; - -namespace psr { - -TEST(TypeGraphTest, AddType) { - LLVMProjectIRDB IRDB( - {unittest::PathToLLTestFiles + "basic/two_structs_cpp.ll"}); - llvm::Module *M = IRDB.getModule(); - - unsigned int NbStruct = 0; - - CachedTypeGraph Tg; - CachedTypeGraph::graph_t G; - - for (auto *StructType : M->getIdentifiedStructTypes()) { - ASSERT_TRUE(StructType != nullptr); - - auto Node = Tg.addType(StructType); - - ASSERT_TRUE(Tg.G[Node].Name == StructType->getName().str()); - ASSERT_TRUE(Tg.G[Node].BaseType == StructType); - ASSERT_TRUE(Tg.G[Node].Types.size() == 1); - ASSERT_TRUE(Tg.G[Node].Types.count(StructType)); - - boost::add_vertex(G); - - ASSERT_TRUE(boost::isomorphism(G, Tg.G)); - - ++NbStruct; - } - - ASSERT_TRUE(NbStruct >= 2); -} - -TEST(TypeGraphTest, ReverseTypePropagation) { - LLVMProjectIRDB IRDB( - {unittest::PathToLLTestFiles + "basic/seven_structs_cpp.ll"}); - llvm::Module *M = IRDB.getModule(); - - unsigned int NbStruct = 0; - llvm::StructType *StructA = nullptr; - - llvm::StructType *StructB = nullptr; - - llvm::StructType *StructC = nullptr; - - llvm::StructType *StructD = nullptr; - - llvm::StructType *StructE = nullptr; - - CachedTypeGraph Tg; - - // Isomorphism to assure that the TypeGraph have the wanted structure - CachedTypeGraph::graph_t G; - - for (auto *StructType : M->getIdentifiedStructTypes()) { - if (StructType) { - switch (NbStruct) { - case 0: - StructA = StructType; - break; - case 1: - StructB = StructType; - break; - case 2: - StructC = StructType; - break; - case 3: - StructD = StructType; - break; - case 4: - StructE = StructType; - break; - case 5: - [[fallthrough]]; - case 6: - break; - default: - // NB: Will always fail but serve to understand where the error come - // from - ASSERT_TRUE(NbStruct < 7); - break; - } - - ++NbStruct; - } - } - - ASSERT_TRUE(NbStruct == 7); - ASSERT_TRUE(StructA != nullptr); - ASSERT_TRUE(StructB != nullptr); - ASSERT_TRUE(StructC != nullptr); - ASSERT_TRUE(StructD != nullptr); - ASSERT_TRUE(StructE != nullptr); - - auto VertexA = boost::add_vertex(G); - auto VertexB = boost::add_vertex(G); - auto VertexC = boost::add_vertex(G); - auto VertexD = boost::add_vertex(G); - auto VertexE = boost::add_vertex(G); - - auto NodeA = Tg.addType(StructA); - auto NodeB = Tg.addType(StructB); - auto NodeC = Tg.addType(StructC); - auto NodeD = Tg.addType(StructD); - auto NodeE = Tg.addType(StructE); - - Tg.addLinkWithoutReversePropagation(StructA, StructB); - Tg.addLinkWithoutReversePropagation(StructB, StructC); - Tg.addLinkWithoutReversePropagation(StructC, StructD); - Tg.addLinkWithoutReversePropagation(StructE, StructB); - - boost::add_edge(VertexA, VertexB, G); - boost::add_edge(VertexB, VertexC, G); - boost::add_edge(VertexC, VertexD, G); - boost::add_edge(VertexE, VertexB, G); - - ASSERT_TRUE(boost::isomorphism(G, Tg.G)); - - auto TgEdges = boost::edges(Tg.G); - - int NumberEdge = 0; - - TgEdges = boost::edges(Tg.G); - for (auto It = TgEdges.first; It != TgEdges.second; ++It) { - ++NumberEdge; - - auto Src = boost::source(*It, Tg.G); - auto Target = boost::target(*It, Tg.G); - - ASSERT_TRUE(NumberEdge <= 4); - - ASSERT_TRUE(Src == NodeA || Src == NodeB || Src == NodeC || Src == NodeE); - if (Src == NodeA) { - ASSERT_TRUE(Target == NodeB); - } else if (Src == NodeB) { - ASSERT_TRUE(Target == NodeC); - } else if (Src == NodeC) { - ASSERT_TRUE(Target == NodeD); - } else if (Src == NodeE) { - ASSERT_TRUE(Target == NodeB); - } - } - - ASSERT_TRUE(NumberEdge == 4); - NumberEdge = 0; // NOLINT Avoid stupid mistakes - - // Check that the type are coherent in the graph - ASSERT_TRUE(Tg.G[VertexA].Types.count(StructA)); - ASSERT_TRUE(Tg.G[VertexA].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexB].Types.count(StructB)); - ASSERT_TRUE(Tg.G[VertexB].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexC].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexC].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexD].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexD].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexE].Types.count(StructE)); - ASSERT_TRUE(Tg.G[VertexE].Types.size() == 1); - - Tg.reverseTypePropagation(StructC); - - // Check that the type are coherent in the graph - ASSERT_TRUE(Tg.G[VertexA].Types.count(StructA) && - Tg.G[VertexA].Types.count(StructB) && - Tg.G[VertexA].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexA].Types.size() == 3); - ASSERT_TRUE(Tg.G[VertexB].Types.count(StructB) && - Tg.G[VertexB].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexB].Types.size() == 2); - ASSERT_TRUE(Tg.G[VertexC].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexC].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexD].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexD].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexE].Types.count(StructE) && - Tg.G[VertexE].Types.count(StructB) && - Tg.G[VertexE].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexE].Types.size() == 3); -} - -TEST(TypeGraphTest, AddLinkSimple) { - LLVMProjectIRDB IRDB( - {unittest::PathToLLTestFiles + "basic/two_structs_cpp.ll"}); - llvm::Module *M = IRDB.getModule(); - - unsigned int NbStruct = 0; - llvm::StructType *StructA = nullptr; - - llvm::StructType *StructB = nullptr; - - CachedTypeGraph Tg; - CachedTypeGraph::graph_t G; - - for (auto *StructType : M->getIdentifiedStructTypes()) { - if (StructType) { - switch (NbStruct) { - case 0: - StructA = StructType; - break; - case 1: - StructB = StructType; - break; - default: - // NB: Will always fail but serve to understand where the error come - // from - ASSERT_TRUE(NbStruct < 2); - break; - } - - ++NbStruct; - } - } - - ASSERT_TRUE(NbStruct == 2); - ASSERT_TRUE(StructA != nullptr); - ASSERT_TRUE(StructB != nullptr); - - auto NodeA = Tg.addType(StructA); - auto NodeB = Tg.addType(StructB); - Tg.addLink(StructA, StructB); - - auto VertexA = boost::add_vertex(G); - auto VertexB = boost::add_vertex(G); - - boost::add_edge(VertexA, VertexB, G); - - ASSERT_TRUE(boost::isomorphism(G, Tg.G)); - - auto P = edges(Tg.G); - - auto Begin = P.first; - auto End = P.second; - - int NumberEdge = 0; - - for (auto It = Begin; It != End; ++It) { - ++NumberEdge; - - auto Src = boost::source(*It, Tg.G); - auto Target = boost::target(*It, Tg.G); - - ASSERT_TRUE(NumberEdge == 1); - ASSERT_TRUE(Src == NodeA); - ASSERT_TRUE(Target == NodeB); - } - - ASSERT_TRUE(NumberEdge == 1); -} - -TEST(TypeGraphTest, TypeAggregation) { - LLVMProjectIRDB IRDB( - {unittest::PathToLLTestFiles + "basic/seven_structs_cpp.ll"}); - llvm::Module *M = IRDB.getModule(); - - unsigned int NbStruct = 0; - llvm::StructType *StructA = nullptr; - llvm::StructType *StructB = nullptr; - llvm::StructType *StructC = nullptr; - llvm::StructType *StructD = nullptr; - llvm::StructType *StructE = nullptr; - - CachedTypeGraph Tg; - - // Isomorphism to assure that the TypeGraph have the wanted structure - CachedTypeGraph::graph_t G; - - for (auto *StructType : M->getIdentifiedStructTypes()) { - if (StructType) { - switch (NbStruct) { - case 0: - StructA = StructType; - break; - case 1: - StructB = StructType; - break; - case 2: - StructC = StructType; - break; - case 3: - StructD = StructType; - break; - case 4: - StructE = StructType; - break; - case 5: - [[fallthrough]]; - case 6: - break; - default: - // NB: Will always fail but serve to understand where the error come - // from - ASSERT_TRUE(NbStruct < 7); - break; - } - - ++NbStruct; - } - } - - ASSERT_TRUE(NbStruct == 7); - ASSERT_TRUE(StructA != nullptr); - ASSERT_TRUE(StructB != nullptr); - ASSERT_TRUE(StructC != nullptr); - ASSERT_TRUE(StructD != nullptr); - ASSERT_TRUE(StructE != nullptr); - - auto VertexA = boost::add_vertex(G); - auto VertexB = boost::add_vertex(G); - auto VertexC = boost::add_vertex(G); - auto VertexD = boost::add_vertex(G); - auto VertexE = boost::add_vertex(G); - - auto NodeA = Tg.addType(StructA); - auto NodeB = Tg.addType(StructB); - auto NodeC = Tg.addType(StructC); - auto NodeD = Tg.addType(StructD); - auto NodeE = Tg.addType(StructE); - - Tg.addLinkWithoutReversePropagation(StructA, StructB); - Tg.addLinkWithoutReversePropagation(StructB, StructC); - Tg.addLinkWithoutReversePropagation(StructC, StructD); - Tg.addLinkWithoutReversePropagation(StructE, StructB); - - boost::add_edge(VertexA, VertexB, G); - boost::add_edge(VertexB, VertexC, G); - boost::add_edge(VertexC, VertexD, G); - boost::add_edge(VertexE, VertexB, G); - - ASSERT_TRUE(boost::isomorphism(G, Tg.G)); - - auto TgEdges = boost::edges(Tg.G); - - int NumberEdge = 0; - - TgEdges = boost::edges(Tg.G); - for (auto It = TgEdges.first; It != TgEdges.second; ++It) { - ++NumberEdge; - - auto Src = boost::source(*It, Tg.G); - auto Target = boost::target(*It, Tg.G); - - ASSERT_TRUE(NumberEdge <= 4); - - ASSERT_TRUE(Src == NodeA || Src == NodeB || Src == NodeC || Src == NodeE); - if (Src == NodeA) { - ASSERT_TRUE(Target == NodeB); - } else if (Src == NodeB) { - ASSERT_TRUE(Target == NodeC); - } else if (Src == NodeC) { - ASSERT_TRUE(Target == NodeD); - } else if (Src == NodeE) { - ASSERT_TRUE(Target == NodeB); - } - } - - ASSERT_TRUE(NumberEdge == 4); - NumberEdge = 0; // NOLINT Avoid stupid mistakes - - // Check that the type are coherent in the graph - ASSERT_TRUE(Tg.G[VertexA].Types.count(StructA)); - ASSERT_TRUE(Tg.G[VertexA].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexB].Types.count(StructB)); - ASSERT_TRUE(Tg.G[VertexB].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexC].Types.count(StructC)); - ASSERT_TRUE(Tg.G[VertexC].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexD].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexD].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexE].Types.count(StructE)); - ASSERT_TRUE(Tg.G[VertexE].Types.size() == 1); - - Tg.aggregateTypes(); - - // Check that the type are coherent in the graph - ASSERT_TRUE(Tg.G[VertexA].Types.count(StructA) && - Tg.G[VertexA].Types.count(StructB) && - Tg.G[VertexA].Types.count(StructC) && - Tg.G[VertexA].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexA].Types.size() == 4); - ASSERT_TRUE(Tg.G[VertexB].Types.count(StructB) && - Tg.G[VertexB].Types.count(StructC) && - Tg.G[VertexB].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexB].Types.size() == 3); - ASSERT_TRUE(Tg.G[VertexC].Types.count(StructC) && - Tg.G[VertexC].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexC].Types.size() == 2); - ASSERT_TRUE(Tg.G[VertexD].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexD].Types.size() == 1); - ASSERT_TRUE(Tg.G[VertexE].Types.count(StructE) && - Tg.G[VertexE].Types.count(StructB) && - Tg.G[VertexE].Types.count(StructC) && - Tg.G[VertexE].Types.count(StructD)); - ASSERT_TRUE(Tg.G[VertexE].Types.size() == 4); -} -} // namespace psr - -int main(int Argc, char **Argv) { - ::testing::InitGoogleTest(&Argc, Argv); - auto Res = RUN_ALL_TESTS(); - llvm::llvm_shutdown(); - return Res; -} From 6e258917a52b2324dae66616cc0256e7e5bacaf4 Mon Sep 17 00:00:00 2001 From: Maximilian Leo Huber Date: Thu, 3 Apr 2025 19:49:02 +0200 Subject: [PATCH 05/24] Default Flow Functions (#768) * IDETabulationProblem child classes * get call, ret, calltoret flowfunc impls * default flow functions * call, ret, calltoret with AliasInfo * Lambda funcs for retflow * PureFlow tests * backup before merge * More Test Files * normal and call flow tests * LLVMAliasSet fix NoAliasImpl * fix + commented out bad code * fixed some NoAliasInfoTabProb code * temp removed broken tests * specific value sets fix * first retflow test * cleanup * fixed NormalFlow01 + ground truth * last tests, seqfaults * good progress, few tests fail * Fix compilation issue after merge * Remove unused-variable warning in release mode for CallGraph.h * tests pass, rebased failed * mionr after rebase * fixed all tests after rebase * Some cleanup * Rename the default problems + add IFDS versions * minor * Some cleanup in ll file generation --------- Co-authored-by: Fabian Schiebel Co-authored-by: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> --- include/phasar/ControlFlow/CallGraph.h | 3 +- .../IfdsIde/DefaultAliasAwareIDEProblem.h | 158 ++++ .../IfdsIde/DefaultNoAliasIDEProblem.h | 124 +++ .../PhasarLLVM/Pointer/FilteredLLVMAliasSet.h | 3 +- .../DefaultAliasAwareIDEFlowFunctions.cpp | 102 +++ .../DefaultNoaliasIDEFlowFunctions.cpp | 87 ++ test/llvm_test_code/pure_flow/CMakeLists.txt | 18 + .../pure_flow/all_flow_functions_01.cpp | 15 + .../pure_flow/call_flow/CMakeLists.txt | 8 + .../pure_flow/call_flow/call_flow_01.cpp | 12 + .../pure_flow/call_flow/call_flow_02.cpp | 23 + .../pure_flow/call_to_ret_flow/CMakeLists.txt | 8 + .../call_to_ret_flow/call_to_ret_flow_01.cpp | 12 + .../call_to_ret_flow/call_to_ret_flow_02.cpp | 19 + .../pure_flow/int_call_no_params_01.cpp | 12 + .../pure_flow/int_call_one_param_01.cpp | 12 + .../pure_flow/int_call_pointer_param_01.cpp | 13 + .../pure_flow/normal_flow/CMakeLists.txt | 10 + .../pure_flow/normal_flow/normal_flow_01.cpp | 9 + .../pure_flow/normal_flow/normal_flow_02.cpp | 14 + .../pure_flow/normal_flow/normal_flow_03.cpp | 16 + .../pure_flow/normal_flow/normal_flow_04.cpp | 15 + .../pure_flow/ret_flow/CMakeLists.txt | 9 + .../pure_flow/ret_flow/ret_flow_01.cpp | 18 + .../pure_flow/ret_flow/ret_flow_02.cpp | 27 + .../pure_flow/ret_flow/ret_flow_03.cpp | 32 + .../pure_flow/void_call_no_params_01.cpp | 8 + .../pure_flow/void_call_one_param_01.cpp | 8 + .../pure_flow/void_call_pointer_param_01.cpp | 9 + .../DataFlow/IfdsIde/CMakeLists.txt | 1 + .../IfdsIde/DefaultFlowFunctionTest.cpp | 852 ++++++++++++++++++ 31 files changed, 1654 insertions(+), 3 deletions(-) create mode 100644 include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h create mode 100644 include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h create mode 100644 lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp create mode 100644 lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp create mode 100644 test/llvm_test_code/pure_flow/CMakeLists.txt create mode 100644 test/llvm_test_code/pure_flow/all_flow_functions_01.cpp create mode 100644 test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt create mode 100644 test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp create mode 100644 test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp create mode 100644 test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt create mode 100644 test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp create mode 100644 test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp create mode 100644 test/llvm_test_code/pure_flow/int_call_no_params_01.cpp create mode 100644 test/llvm_test_code/pure_flow/int_call_one_param_01.cpp create mode 100644 test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp create mode 100644 test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt create mode 100644 test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp create mode 100644 test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp create mode 100644 test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp create mode 100644 test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp create mode 100644 test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt create mode 100644 test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp create mode 100644 test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp create mode 100644 test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp create mode 100644 test/llvm_test_code/pure_flow/void_call_no_params_01.cpp create mode 100644 test/llvm_test_code/pure_flow/void_call_one_param_01.cpp create mode 100644 test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp create mode 100644 unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index e2a03fec25..79350de642 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -177,8 +177,7 @@ template class CallGraphBuilder { [[nodiscard]] FunctionVertexTy *addFunctionVertex(f_t Fun) { auto [It, Inserted] = CG.CallersOf.try_emplace(std::move(Fun), nullptr); if (Inserted) { - auto Cap = CG.FunVertexOwner.capacity(); - assert(CG.FunVertexOwner.size() < Cap && + assert(CG.FunVertexOwner.size() < CG.FunVertexOwner.capacity() && "Trying to add more than MaxNumFunctions Function Vertices"); It->second = &CG.FunVertexOwner.emplace_back(); } diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h new file mode 100644 index 0000000000..9a73977f97 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -0,0 +1,158 @@ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +#include + +// Forward declaration of types for which we only use its pointer or ref type +namespace llvm { +class Instruction; +class Function; +class Value; +} // namespace llvm + +namespace psr { + +namespace detail { +class IDEAliasAwareDefaultFlowFunctionsImpl + : private IDENoAliasDefaultFlowFunctionsImpl { +public: + using typename IDENoAliasDefaultFlowFunctionsImpl::d_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::f_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionPtrType; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionType; + using typename IDENoAliasDefaultFlowFunctionsImpl::n_t; + + using IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled; + + [[nodiscard]] constexpr LLVMAliasInfoRef getAliasInfo() const noexcept { + return AS; + } + + constexpr IDEAliasAwareDefaultFlowFunctionsImpl(LLVMAliasInfoRef AS) noexcept + : AS(AS) { + assert(AS && "You must provide an alias information handle!"); + } + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, + n_t /*Succ*/); + [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, + f_t CalleeFun); + [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, + f_t /*CalleeFun*/, + n_t ExitInst, + n_t /*RetSite*/); + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunctionImpl(n_t CallSite, n_t /*RetSite*/, + llvm::ArrayRef /*Callees*/); + +private: + LLVMAliasInfoRef AS; +}; +} // namespace detail + +template +class DefaultAliasAwareIDEProblem + : public IDETabulationProblem, + protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { +public: + using ProblemAnalysisDomain = AnalysisDomainTy; + using d_t = typename AnalysisDomainTy::d_t; + using n_t = typename AnalysisDomainTy::n_t; + using f_t = typename AnalysisDomainTy::f_t; + using t_t = typename AnalysisDomainTy::t_t; + using v_t = typename AnalysisDomainTy::v_t; + using l_t = typename AnalysisDomainTy::l_t; + 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; + + using container_type = typename FlowFunctionType::container_type; + + /// Constructs an IDETabulationProblem 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 DefaultAliasAwareIDEProblem( + const ProjectIRDBBase *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::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: + /// 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 ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + std::vector EntryPoints, + d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) + : IFDSTabulationProblem(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); + } +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h new file mode 100644 index 0000000000..1865f749f2 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h @@ -0,0 +1,124 @@ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H +#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/Domain/LLVMAnalysisDomain.h" + +namespace llvm { +class Value; +class Instruction; +class Function; +} // namespace llvm + +namespace psr { + +namespace detail { +class IDENoAliasDefaultFlowFunctionsImpl { +public: + using d_t = const llvm::Value *; + using n_t = const llvm::Instruction *; + using f_t = const llvm::Function *; + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; + + virtual ~IDENoAliasDefaultFlowFunctionsImpl() = default; + + /// True, if the analysis knows this function, either because it is analyzed, + /// or because we have external information about it. + [[nodiscard]] virtual bool isFunctionModeled(f_t Fun) const; + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, + n_t /*Succ*/); + [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, + f_t CalleeFun); + [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, + f_t /*CalleeFun*/, + n_t ExitInst, + n_t /*RetSite*/); + [[nodiscard]] FlowFunctionPtrType + getCallToRetFlowFunctionImpl(n_t CallSite, n_t /*RetSite*/, + llvm::ArrayRef /*Callees*/); +}; +} // namespace detail + +template +class DefaultNoAliasIDEProblem + : public IDETabulationProblem, + protected detail::IDENoAliasDefaultFlowFunctionsImpl { +public: + using ProblemAnalysisDomain = AnalysisDomainTy; + using d_t = typename AnalysisDomainTy::d_t; + using n_t = typename AnalysisDomainTy::n_t; + using f_t = typename AnalysisDomainTy::f_t; + using t_t = typename AnalysisDomainTy::t_t; + using v_t = typename AnalysisDomainTy::v_t; + using l_t = typename AnalysisDomainTy::l_t; + 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; + + using IDETabulationProblem::IDETabulationProblem; + + [[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 DefaultNoAliasIFDSProblem + : public IFDSTabulationProblem, + protected detail::IDENoAliasDefaultFlowFunctionsImpl { +public: + using IFDSTabulationProblem::IFDSTabulationProblem; + + [[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); + } +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h index 91599637c9..b18cadbecb 100644 --- a/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h @@ -66,7 +66,8 @@ class FilteredLLVMAliasSet { typename = std::enable_if_t< std::is_constructible_v>> explicit FilteredLLVMAliasSet(ArgsT &&...Args) - : FilteredLLVMAliasSet(std::forward(Args)...) {} + : FilteredLLVMAliasSet( + std::make_unique(std::forward(Args)...)) {} // --- API Functions: diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp new file mode 100644 index 0000000000..ed18352cee --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp @@ -0,0 +1,102 @@ +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +#include "llvm/IR/Instructions.h" + +using namespace psr; + +using FFTemplates = + FlowFunctionTemplates; +using container_type = FFTemplates::container_type; + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + n_t Curr, n_t Succ) -> FlowFunctionPtrType { + + if (const auto *Store = llvm::dyn_cast(Curr)) { + container_type Gen; + + auto AliasSet = AS.getAliasSet(Store->getPointerOperand(), Store); + Gen.insert(AliasSet->begin(), AliasSet->end()); + Gen.insert(Store->getValueOperand()); + + return FFTemplates::lambdaFlow( + [Store, Gen{std::move(Gen)}](d_t Source) -> container_type { + if (Store->getPointerOperand() == Source) { + return {}; + } + if (Store->getValueOperand() == Source) { + return Gen; + } + + return {Source}; + }); + } + + return this->IDENoAliasDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + Curr, Succ); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + n_t CallInst, f_t CalleeFun) -> FlowFunctionPtrType { + return this->IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + CallInst, CalleeFun); +} + +static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, + const llvm::Instruction *Context) { + container_type Tmp = Facts; + for (const auto *Fact : Facts) { + auto Aliases = AS.getAliasSet(Fact, Context); + for (const auto *Alias : *Aliases) { + if (const auto *Inst = llvm::dyn_cast(Alias)) { + if (Inst->getParent() == Context->getParent() && + Context->comesBefore(Inst)) { + // We will see that inst later + continue; + } + } + + if (const auto *Load = llvm::dyn_cast(Alias)) { + // Handle at least one level of indirection... + const auto *PointerOp = Load->getPointerOperand()->stripPointerCasts(); + Tmp.insert(PointerOp); + } + + Tmp.insert(Alias); + } + } + + Facts = std::move(Tmp); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( + n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, n_t /*RetSite*/) + -> FlowFunctionPtrType { + container_type Gen; + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + const auto PostProcessFacts = [AS = AS, Call](container_type &Facts) { + populateWithMayAliases(AS, Facts, Call); + }; + + return mapFactsToCaller( + Call, ExitInst, + [](d_t Param, d_t Source) { + return Param == Source && Param->getType()->isPointerTy(); + }, + {}, {}, true, true, PostProcessFacts); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDEAliasAwareDefaultFlowFunctionsImpl:: + getCallToRetFlowFunctionImpl(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) + -> FlowFunctionPtrType { + return this->IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl( + CallSite, RetSite, Callees); +} diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp new file mode 100644 index 0000000000..538fb22924 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultNoaliasIDEFlowFunctions.cpp @@ -0,0 +1,87 @@ +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Operator.h" + +using namespace psr; + +using FFTemplates = + FlowFunctionTemplates; + +bool detail::IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled( + f_t Fun) const { + return !Fun->isDeclaration(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + n_t Curr, n_t /*Succ*/) -> FlowFunctionPtrType { + + if (const auto *Store = llvm::dyn_cast(Curr)) { + return strongUpdateStore(Store); + } + if (const auto *Load = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlowIf(Load, [Load](d_t Source) { + return Source == Load->getPointerOperand(); + }); + } + if (const auto *UnaryOp = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlow(UnaryOp, UnaryOp->getOperand(0)); + } + if (const auto *BinaryOp = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlowIf(BinaryOp, [BinaryOp](d_t Source) { + return Source == BinaryOp->getOperand(0) || + Source == BinaryOp->getOperand(1); + }); + } + if (const auto *GetElementPtr = llvm::dyn_cast(Curr)) { + return FFTemplates::generateFlow(GetElementPtr, + GetElementPtr->getPointerOperand()); + } + + return FFTemplates::identityFlow(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( + n_t CallInst, f_t CalleeFun) -> FlowFunctionPtrType { + if (const auto *CallSite = llvm::dyn_cast(CallInst)) { + return mapFactsToCallee(CallSite, CalleeFun); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( + n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, n_t /*RetSite*/) + -> FlowFunctionPtrType { + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + return mapFactsToCaller(Call, ExitInst, [](d_t Param, d_t Source) { + return Param == Source && Param->getType()->isPointerTy(); + }); + } + + return FFTemplates::killAllFlows(); +} + +auto detail::IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl( + n_t CallSite, n_t /*RetSite*/, llvm::ArrayRef Callees) + -> FlowFunctionPtrType { + + if (llvm::any_of(Callees, + [this](f_t Callee) { return !isFunctionModeled(Callee); })) { + // Cannot strongly update, if we don't know the callee + return FFTemplates::identityFlow(); + } + + if (const auto *Call = llvm::dyn_cast(CallSite)) { + return mapFactsAlongsideCallSite( + Call, [](d_t Arg) { return !Arg->getType()->isPointerTy(); }, false); + } + + return FFTemplates::identityFlow(); +} diff --git a/test/llvm_test_code/pure_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/CMakeLists.txt new file mode 100644 index 0000000000..60b08c0034 --- /dev/null +++ b/test/llvm_test_code/pure_flow/CMakeLists.txt @@ -0,0 +1,18 @@ +set(NoMem2regSources + all_flow_functions_01.cpp + int_call_no_params_01.cpp + int_call_one_param_01.cpp + int_call_pointer_param_01.cpp + void_call_no_params_01.cpp + void_call_one_param_01.cpp + void_call_pointer_param_01.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) + +subdirlist(subdirs ${CMAKE_CURRENT_SOURCE_DIR}) +foreach(subdir ${subdirs}) + add_subdirectory(${subdir}) +endforeach(subdir) diff --git a/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp b/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp new file mode 100644 index 0000000000..0cf43f662d --- /dev/null +++ b/test/llvm_test_code/pure_flow/all_flow_functions_01.cpp @@ -0,0 +1,15 @@ +#include + +int call(const int *One, const int Second) { + int ToReturn = 1; + printf("call(%d, %d)\nreturn %d", *One, Second, ToReturn); + return ToReturn; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + int ReturnInt = call(&One, Two); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt new file mode 100644 index 0000000000..5798e97fbf --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/CMakeLists.txt @@ -0,0 +1,8 @@ +set(NoMem2regSources + call_flow_01.cpp + call_flow_02.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp b/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp new file mode 100644 index 0000000000..e21f965083 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/call_flow_01.cpp @@ -0,0 +1,12 @@ +#include + +void call(int ZeroArg, int OneArg) {} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + call(Zero, One); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp b/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp new file mode 100644 index 0000000000..90c5f6304f --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_flow/call_flow_02.cpp @@ -0,0 +1,23 @@ +#include + +void call(int *One) {} +void secondCall(int *One, int **Two, int ***Three) {} + +int main(int /*argc*/, char * /*argv*/[]) { + + int One = 1; + int Two = 2; + int Three = 3; + + int *PtrToOne = &One; + int *PtrToTwo = &Two; + int **PtrPtrToTwo = &PtrToTwo; + int *PtrToThree = &Three; + int **PtrPtrToThree = &PtrToThree; + int ***PtrPtrPtrToThree = &PtrPtrToThree; + + call(PtrToOne); + secondCall(PtrToOne, PtrPtrToTwo, PtrPtrPtrToThree); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt new file mode 100644 index 0000000000..36d041b6ed --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/CMakeLists.txt @@ -0,0 +1,8 @@ +set(NoMem2regSources + call_to_ret_flow_01.cpp + call_to_ret_flow_02.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp new file mode 100644 index 0000000000..1797a073c7 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_01.cpp @@ -0,0 +1,12 @@ +#include + +int call(int Zero, int One) { return Zero + One; } + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp new file mode 100644 index 0000000000..4d014bcfa3 --- /dev/null +++ b/test/llvm_test_code/pure_flow/call_to_ret_flow/call_to_ret_flow_02.cpp @@ -0,0 +1,19 @@ +#include + +void callTwo() {} +void callOne() {} +void call() { + int Three = 3; + callOne(); + int Four = 4; + callTwo(); +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + call(); + int Two = 2; + callOne(); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp b/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp new file mode 100644 index 0000000000..d2b3277a75 --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_no_params_01.cpp @@ -0,0 +1,12 @@ +#include + +int call() { + printf("call()\n"); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int ReturnInt = call(); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp b/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp new file mode 100644 index 0000000000..97912f62fe --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_one_param_01.cpp @@ -0,0 +1,12 @@ +#include + +int call(int Integer) { + printf("call(%d)\n", Integer); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int ReturnInt = call(1); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp b/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp new file mode 100644 index 0000000000..0d7a114460 --- /dev/null +++ b/test/llvm_test_code/pure_flow/int_call_pointer_param_01.cpp @@ -0,0 +1,13 @@ +#include + +int call(const int *Integer) { + printf("call(%d)\n", *Integer); + return 1; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int ReturnInt = call(&One); + printf("ReturnInt: %d\n", ReturnInt); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt new file mode 100644 index 0000000000..225ec21a61 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/CMakeLists.txt @@ -0,0 +1,10 @@ +set(NoMem2regSources + normal_flow_01.cpp + normal_flow_02.cpp + normal_flow_03.cpp + normal_flow_04.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp new file mode 100644 index 0000000000..a788a4187c --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_01.cpp @@ -0,0 +1,9 @@ +#include + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + int *OnePtr = &One; + int &TwoAddr = Two; + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp new file mode 100644 index 0000000000..965f099801 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_02.cpp @@ -0,0 +1,14 @@ +#include + +void call() {} + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int Two = 2; + + call(); + + int Three = 3; + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp new file mode 100644 index 0000000000..2bcbe573ba --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_03.cpp @@ -0,0 +1,16 @@ +#include + +struct StructOne { + int One = 1; +}; + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + int OtherOne = One; + int MinusOne = !One; + int Two = One + One; + int *PtrToOne = &One; + StructOne ForGEP = StructOne(); + int GEP = ForGEP.One; + return 0; +} diff --git a/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp new file mode 100644 index 0000000000..9012516ff0 --- /dev/null +++ b/test/llvm_test_code/pure_flow/normal_flow/normal_flow_04.cpp @@ -0,0 +1,15 @@ +#include + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + + int *PtrToOne = &One; + int **PtrPtrToOne = &PtrToOne; + int ***PtrPtrPtrToOne = &PtrPtrToOne; + + int Deref1 = *PtrToOne; + int Deref2 = **PtrPtrToOne; + int Deref3 = ***PtrPtrPtrToOne; + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt b/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt new file mode 100644 index 0000000000..018af3009b --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/CMakeLists.txt @@ -0,0 +1,9 @@ +set(NoMem2regSources + ret_flow_01.cpp + ret_flow_02.cpp + ret_flow_03.cpp +) + +foreach(TEST_SRC ${NoMem2regSources}) + generate_ll_file(FILE ${TEST_SRC} DEBUG) +endforeach(TEST_SRC) diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp new file mode 100644 index 0000000000..a3e3d0a6a0 --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_01.cpp @@ -0,0 +1,18 @@ +#include + +int getTwo() { return 2; } + +int call(int ZeroArg, int OneArg) { + int TwoInCall = getTwo(); + return ZeroArg + OneArg; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp new file mode 100644 index 0000000000..06dc62def8 --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_02.cpp @@ -0,0 +1,27 @@ +#include + +int getTwo() { return 2; } + +int newThree() { + int FourInFunc = 4; + return 3; +} + +int call(int ZeroArg, int OneArg) { + int TwoInCall = getTwo(); + int ThreeInCall = 3; + + ThreeInCall = newThree(); + + return ZeroArg + OneArg; +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp new file mode 100644 index 0000000000..d540ffc27b --- /dev/null +++ b/test/llvm_test_code/pure_flow/ret_flow/ret_flow_03.cpp @@ -0,0 +1,32 @@ +#include + +int GlobalFour = 4; + +const int *newThree(const int *ThreeArg) { + const int *NewThreePtrInFunc = &(*ThreeArg); + return NewThreePtrInFunc; +} + +int *getFourPtr() { return &GlobalFour; } + +int &getFourAddr() { return GlobalFour; } + +int call(int &ZeroArg, const int *OneArg) { + int TwoInCall = 2; + int ThreeInCall = 3; + int *ThreePtrInCall = &ThreeInCall; + const int *NewThreeInCall = newThree(ThreePtrInCall); + + return ZeroArg + *OneArg + TwoInCall + *NewThreeInCall + *getFourPtr() + + getFourAddr(); +} + +int main(int /*argc*/, char * /*argv*/[]) { + int Zero = 0; + int One = 1; + int Two = 2; + + int CallReturn = call(Zero, &One); + + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp b/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp new file mode 100644 index 0000000000..3c3111e249 --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_no_params_01.cpp @@ -0,0 +1,8 @@ +#include + +void call() { printf("call()"); } + +int main(int /*argc*/, char * /*argv*/[]) { + call(); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp b/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp new file mode 100644 index 0000000000..71589e5f83 --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_one_param_01.cpp @@ -0,0 +1,8 @@ +#include + +void call(int Integer) { printf("call(%d)\n", Integer); } + +int main(int /*argc*/, char * /*argv*/[]) { + call(1); + return 0; +} diff --git a/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp b/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp new file mode 100644 index 0000000000..0b181aee9d --- /dev/null +++ b/test/llvm_test_code/pure_flow/void_call_pointer_param_01.cpp @@ -0,0 +1,9 @@ +#include + +void call(const int *Integer) { printf("call(%d)\n", *Integer); } + +int main(int /*argc*/, char * /*argv*/[]) { + int One = 1; + call(&One); + return 0; +} diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt index 5c191d3856..dcda34ffdd 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt @@ -1,6 +1,7 @@ add_subdirectory(Problems) set(IfdsIdeSources + DefaultFlowFunctionTest.cpp EdgeFunctionComposerTest.cpp EdgeFunctionSingletonCacheTest.cpp InteractiveIDESolverTest.cpp diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp new file mode 100644 index 0000000000..71d12e3129 --- /dev/null +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp @@ -0,0 +1,852 @@ +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/raw_ostream.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +#include + +using namespace psr; + +namespace { + +class IDEAliasImpl : public DefaultAliasAwareIFDSProblem { +public: + IDEAliasImpl(LLVMProjectIRDB *IRDB) + : DefaultAliasAwareIFDSProblem(IRDB, &PT, {}, {}), PT(IRDB){}; + + [[nodiscard]] InitialSeeds initialSeeds() override { + return {}; + }; + +private: + FilteredLLVMAliasSet PT; +}; + +class IDENoAliasImpl : public DefaultNoAliasIFDSProblem { +public: + IDENoAliasImpl(LLVMProjectIRDB *IRDB) + : DefaultNoAliasIFDSProblem(IRDB, {}, {}){}; + + [[nodiscard]] InitialSeeds initialSeeds() override { + return {}; + }; +}; + +std::set +getNormalFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg) { + const auto AliasNormalFlowFunc = + AliasImpl.getNormalFlowFunction(Instr, nullptr); + const auto AliasLLVMValueSet = AliasNormalFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getNormalFlowValueSet(const llvm::Instruction *Instr, + IDENoAliasImpl &NoAliasImpl, const llvm::Value *Arg) { + const auto NoAliasNormalFlowFunc = + NoAliasImpl.getNormalFlowFunction(Instr, nullptr); + const auto NoAliasLLVMValueSet = NoAliasNormalFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getCallFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg, const llvm::Function *CalleeFunc) { + const auto AliasCallFlowFunc = + AliasImpl.getCallFlowFunction(Instr, CalleeFunc); + std::set AliasLLVMValueSet = + AliasCallFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getCallFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, + const llvm::Value *Arg, const llvm::Function *CalleeFunc) { + const auto NoAliasCallFlowFunc = + NoAliasImpl.getCallFlowFunction(Instr, CalleeFunc); + std::set NoAliasLLVMValueSet = + NoAliasCallFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getRetFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, + const llvm::Value *Arg, const llvm::Instruction *ExitInst) { + const auto AliasRetFlowFunc = + AliasImpl.getRetFlowFunction(Instr, nullptr, ExitInst, nullptr); + std::set AliasLLVMValueSet = + AliasRetFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getRetFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, + const llvm::Value *Arg, const llvm::Instruction *ExitInst) { + const auto NoAliasRetFlowFunc = + NoAliasImpl.getRetFlowFunction(Instr, nullptr, ExitInst, nullptr); + std::set NoAliasLLVMValueSet = + NoAliasRetFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::set +getCallToRetFlowValueSet(const llvm::Instruction *Instr, + IDEAliasImpl &AliasImpl, const llvm::Value *Arg) { + const auto AliasCallToRetFlowFunc = + AliasImpl.getCallToRetFlowFunction(Instr, nullptr, {}); + const auto AliasLLVMValueSet = AliasCallToRetFlowFunc->computeTargets(Arg); + return AliasLLVMValueSet; +} + +std::set +getCallToRetFlowValueSet(const llvm::Instruction *Instr, + IDENoAliasImpl &NoAliasImpl, const llvm::Value *Arg) { + const auto NoAliasCallToRetFlowFunc = + NoAliasImpl.getCallToRetFlowFunction(Instr, nullptr, {}); + const auto NoAliasLLVMValueSet = + NoAliasCallToRetFlowFunc->computeTargets(Arg); + return NoAliasLLVMValueSet; +} + +std::string stringifyValueSet(const std::set &Vals) { + std::string Ret; + llvm::raw_string_ostream ROS(Ret); + + ROS << "{ "; + + llvm::interleaveComma( + Vals, ROS, [&ROS](const auto *Val) { ROS << llvmIRToString(Val); }); + + ROS << " }"; + + return Ret; +} + +TEST(PureFlow, NormalFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + const auto *MainFunc = IRDB.getFunction("main"); + // %0 + const auto *PercentZero = MainFunc->getArg(0); + // %1 + const auto *PercentOne = MainFunc->getArg(1); + // %.addr = alloca i32, align 4, !psr.id !216; | ID: 1 + const auto *Instr1 = IRDB.getInstruction(1); + // %.addr1 = alloca ptr, align 8, !psr.id !217; | ID: 2 + const auto *Instr2 = IRDB.getInstruction(2); + //%One = alloca i32, align 4, !psr.id !218; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + // %Two = alloca i32, align 4, !psr.id !219; | ID: 4 + const auto *Instr4 = IRDB.getInstruction(4); + // %OnePtr = alloca ptr, align 8, !psr.id !220; | ID: 5 + const auto *Instr5 = IRDB.getInstruction(5); + // %TwoAddr = alloca ptr, align 8, !psr.id !221; | ID: 6 + const auto *Instr6 = IRDB.getInstruction(6); + + // store i32 %0, ptr %.addr, align 4 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + EXPECT_EQ((std::set{PercentZero, Instr1}), + getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr1}), + getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr8, AliasImpl, Instr1)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr8, NoAliasImpl, Instr1)); + + // store ptr %1, ptr %.addr1, align 8 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + EXPECT_EQ((std::set{PercentOne, Instr2}), + getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr2}), + getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr10, AliasImpl, Instr2)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr2)); + + // store ptr %One, ptr %OnePtr, align 8, !dbg !225 + const auto *Instr17 = IRDB.getInstruction(17); + ASSERT_TRUE(Instr17); + EXPECT_EQ((std::set{Instr3, Instr5}), + getNormalFlowValueSet(Instr17, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr5}), + getNormalFlowValueSet(Instr17, NoAliasImpl, Instr3)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr17, AliasImpl, Instr5)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr17, NoAliasImpl, Instr5)); + + // store ptr %Two, ptr %TwoAddr, align 8, !dbg !228 + const auto *Instr19 = IRDB.getInstruction(19); + ASSERT_TRUE(Instr19); + EXPECT_EQ((std::set{Instr4, Instr6}), + getNormalFlowValueSet(Instr19, AliasImpl, Instr4)); + EXPECT_EQ((std::set{Instr4, Instr6}), + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr4)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr19, AliasImpl, Instr6)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr6)); + + // Other arg + EXPECT_EQ(std::set{Instr19}, + getNormalFlowValueSet(Instr19, AliasImpl, Instr19)); + EXPECT_EQ(std::set{Instr19}, + getNormalFlowValueSet(Instr19, NoAliasImpl, Instr19)); +} + +TEST(PureFlow, NormalFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + const auto *MainFunc = IRDB.getFunction("main"); + ASSERT_TRUE(MainFunc); + // %0 + const auto *PercentZero = MainFunc->getArg(0); + ASSERT_TRUE(PercentZero); + // %1 + const auto *PercentOne = MainFunc->getArg(1); + ASSERT_TRUE(PercentOne); + // %.addr = alloca i32, align 4, !psr.id !221; | ID: 2 + const auto *Instr2 = IRDB.getInstruction(2); + ASSERT_TRUE(Instr2); + // %.addr1 = alloca ptr, align 8, !psr.id !222; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + ASSERT_TRUE(Instr3); + + // store i32 %0, ptr %.addr, align 4, !psr.id !227; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + EXPECT_EQ((std::set{PercentZero, Instr2}), + getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr2}), + getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr8, AliasImpl, Instr2)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr8, NoAliasImpl, Instr2)); + + // store ptr %1, ptr %.addr1, align 8, !psr.id !231; | ID: 10 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + EXPECT_EQ((std::set{PercentOne, Instr3}), + getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr3}), + getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr10, AliasImpl, Instr3)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr3)); + + // Other arg + EXPECT_EQ(std::set{Instr10}, + getNormalFlowValueSet(Instr10, AliasImpl, Instr10)); + EXPECT_EQ(std::set{Instr10}, + getNormalFlowValueSet(Instr10, NoAliasImpl, Instr10)); +} + +TEST(PureFlow, NormalFlow03) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_03_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %One = alloca i32, align 4, !psr.id !222; | ID: 3 + const auto *Instr3 = IRDB.getInstruction(3); + ASSERT_TRUE(Instr3); + // %ForGEP = alloca %struct.StructOne, align 4, !psr.id !227; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + // %2 = load i32, ptr %One, align 4, !dbg !245, !psr.id !246; | ID: 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + + EXPECT_EQ((std::set{Instr3, Instr18}), + getNormalFlowValueSet(Instr18, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr18}), + getNormalFlowValueSet(Instr18, NoAliasImpl, Instr3)); + // %3 = load i32, ptr %One, align 4, !dbg !251, !psr.id !252; | ID: 21 + const auto *Instr21 = IRDB.getInstruction(21); + ASSERT_TRUE(Instr21); + EXPECT_EQ((std::set{Instr3, Instr21}), + getNormalFlowValueSet(Instr21, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr21}), + getNormalFlowValueSet(Instr21, NoAliasImpl, Instr3)); + + // %tobool = icmp ne i32 %3, 0, !dbg !251, !psr.id !253; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + // %lnot = xor i1 %tobool, true, !dbg !254, !psr.id !255; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + EXPECT_EQ((std::set{Instr22, Instr23}), + getNormalFlowValueSet(Instr23, AliasImpl, Instr22)); + EXPECT_EQ((std::set{Instr22, Instr23}), + getNormalFlowValueSet(Instr23, NoAliasImpl, Instr22)); + + // %4 = load i32, ptr %One, align 4, !dbg !261, !psr.id !262; | ID: 27 + const auto *Instr27 = IRDB.getInstruction(27); + ASSERT_TRUE(Instr27); + EXPECT_EQ((std::set{Instr3, Instr27}), + getNormalFlowValueSet(Instr27, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr27}), + getNormalFlowValueSet(Instr27, NoAliasImpl, Instr3)); + + // %5 = load i32, ptr %One, align 4, !dbg !263, !psr.id !264; | ID: 28 + const auto *Instr28 = IRDB.getInstruction(28); + ASSERT_TRUE(Instr28); + EXPECT_EQ((std::set{Instr3, Instr28}), + getNormalFlowValueSet(Instr28, AliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr28}), + getNormalFlowValueSet(Instr28, NoAliasImpl, Instr3)); + + // %add = add nsw i32 %4, %5, !dbg !265, !psr.id !266; | ID: 29 + const auto *Instr29 = IRDB.getInstruction(29); + ASSERT_TRUE(Instr29); + EXPECT_EQ((std::set{Instr27, Instr29}), + getNormalFlowValueSet(Instr29, AliasImpl, Instr27)); + EXPECT_EQ((std::set{Instr27, Instr29}), + getNormalFlowValueSet(Instr29, NoAliasImpl, Instr27)); + EXPECT_EQ((std::set{Instr28, Instr29}), + getNormalFlowValueSet(Instr29, AliasImpl, Instr28)); + EXPECT_EQ((std::set{Instr28, Instr29}), + getNormalFlowValueSet(Instr29, NoAliasImpl, Instr28)); + + // %One2 = getelementptr inbounds %struct.StructOne, ptr %ForGEP, i32 0, i32 + // 0, !dbg !282, !psr.id !283; | ID: 37 + const auto *Instr37 = IRDB.getInstruction(37); + ASSERT_TRUE(Instr37); + EXPECT_EQ((std::set{Instr8, Instr37}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr8)); + EXPECT_EQ((std::set{Instr8, Instr37}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr8)); + + // %6 = load i32, ptr %One2, align 4, !dbg !282, !psr.id !284; | ID: 38 + const auto *Instr38 = IRDB.getInstruction(38); + ASSERT_TRUE(Instr38); + EXPECT_EQ((std::set{Instr37, Instr38}), + getNormalFlowValueSet(Instr38, AliasImpl, Instr37)); + EXPECT_EQ((std::set{Instr37, Instr38}), + getNormalFlowValueSet(Instr38, NoAliasImpl, Instr37)); +} + +TEST(PureFlow, NormalFlow04) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/normal_flow/normal_flow_04_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Deref1 = alloca i32, align 4, !psr.id !222; | ID: 7 + const auto *Instr7 = IRDB.getInstruction(7); + ASSERT_TRUE(Instr7); + // %Deref2 = alloca i32, align 4, !psr.id !223; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + // %Deref3 = alloca i32, align 4, !psr.id !224; | ID: 9 + const auto *Instr9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instr9); + // %3 = load i32, ptr %2, align 4, !dbg !258, !psr.id !259; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + // %6 = load i32, ptr %5, align 4, !dbg !268, !psr.id !269; | ID: 30 + const auto *Instr30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instr30); + // %10 = load i32, ptr %9, align 4, !dbg !280, !psr.id !281; | ID: 36 + const auto *Instr36 = IRDB.getInstruction(36); + ASSERT_TRUE(Instr36); + + // store i32 %3, ptr %Deref1, align 4, !dbg !254, !psr.id !260; | ID: 26 + const auto *Instr26 = IRDB.getInstruction(26); + ASSERT_TRUE(Instr26); + EXPECT_EQ((std::set{Instr25, Instr7}), + getNormalFlowValueSet(Instr26, AliasImpl, Instr25)); + EXPECT_EQ((std::set{Instr25, Instr7}), + getNormalFlowValueSet(Instr26, NoAliasImpl, Instr25)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr26, AliasImpl, Instr7)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr26, NoAliasImpl, Instr7)); + + // store i32 %6, ptr %Deref2, align 4, !dbg !262, !psr.id !270; | ID: 31 + const auto *Instr31 = IRDB.getInstruction(31); + ASSERT_TRUE(Instr31); + EXPECT_EQ((std::set{Instr8, Instr30}), + getNormalFlowValueSet(Instr31, AliasImpl, Instr30)); + EXPECT_EQ((std::set{Instr8, Instr30}), + getNormalFlowValueSet(Instr31, NoAliasImpl, Instr30)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr31, AliasImpl, Instr8)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr31, NoAliasImpl, Instr8)); + + // store i32 %10, ptr %Deref3, align 4, !dbg !272, !psr.id !282; | ID: 37 + const auto *Instr37 = IRDB.getInstruction(37); + ASSERT_TRUE(Instr37); + EXPECT_EQ((std::set{Instr9, Instr36}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr36)); + EXPECT_EQ((std::set{Instr9, Instr36}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr36)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr37, AliasImpl, Instr9)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr37, NoAliasImpl, Instr9)); +} + +/* + CallFlow +*/ + +TEST(PureFlow, CallFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/call_flow/call_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // call void @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg !261, !psr.id + // !262; | ID: 26 + const auto *Instr26 = IRDB.getInstruction(26); + ASSERT_TRUE(Instr26); + const auto *FuncForInstr26 = IRDB.getFunction("_Z4callii"); + ASSERT_TRUE(FuncForInstr26); + ASSERT_EQ(FuncForInstr26->arg_size(), 2); + const auto *Param0 = FuncForInstr26->getArg(0); + const auto *Param1 = FuncForInstr26->getArg(1); + + if (const auto *CallSite = llvm::dyn_cast(Instr26)) { + EXPECT_EQ(std::set{Param0}, + getCallFlowValueSet(Instr26, AliasImpl, + CallSite->getArgOperand(0), FuncForInstr26)); + EXPECT_EQ(std::set{Param1}, + getCallFlowValueSet(Instr26, AliasImpl, + CallSite->getArgOperand(1), FuncForInstr26)); + EXPECT_EQ(std::set{Param0}, + getCallFlowValueSet(Instr26, NoAliasImpl, + CallSite->getArgOperand(0), FuncForInstr26)); + EXPECT_EQ(std::set{Param1}, + getCallFlowValueSet(Instr26, NoAliasImpl, + CallSite->getArgOperand(1), FuncForInstr26)); + } else { + FAIL(); + } +} + +TEST(PureFlow, CallFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/call_flow/call_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %One = alloca i32, align 4, !psr.id !251; | ID: 17 + const auto *Instr17 = IRDB.getInstruction(17); + ASSERT_TRUE(Instr17); + // %Two = alloca i32, align 4, !psr.id !252; | ID: 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + // %Three = alloca i32, align 4, !psr.id !253; | ID: 19 + const auto *Instr19 = IRDB.getInstruction(19); + ASSERT_TRUE(Instr19); + // %PtrToOne = alloca ptr, align 8, !psr.id !254; | ID: 20 + const auto *Instr20 = IRDB.getInstruction(20); + ASSERT_TRUE(Instr20); + // %PtrToTwo = alloca ptr, align 8, !psr.id !255; | ID: 21 + const auto *Instr21 = IRDB.getInstruction(21); + ASSERT_TRUE(Instr21); + // %PtrPtrToTwo = alloca ptr, align 8, !psr.id !256; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + // %PtrToThree = alloca ptr, align 8, !psr.id !257; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + // %PtrPtrToThree = alloca ptr, align 8, !psr.id !258; | ID: 24 + const auto *Instr24 = IRDB.getInstruction(24); + ASSERT_TRUE(Instr24); + // %PtrPtrPtrToThree = alloca ptr, align 8, !psr.id !259; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + // call void @_Z4callPi(ptr noundef %2), !dbg !307, !psr.id !308; | ID: 50 + const auto *Instr50 = IRDB.getInstruction(50); + ASSERT_TRUE(Instr50); + // call void @_Z10secondCallPiPS_PS0_(ptr noundef %3, ptr noundef %4, ptr + // noundef %5), !dbg !315, !psr.id !316; | ID: 54 + const auto *Instr54 = IRDB.getInstruction(54); + ASSERT_TRUE(Instr54); + + // call function + const auto *CallFunc = IRDB.getFunction("_Z4callPi"); + ASSERT_TRUE(CallFunc); + // second call function + const auto *SecondCallFunc = IRDB.getFunction("_Z10secondCallPiPS_PS0_"); + ASSERT_TRUE(SecondCallFunc); + + const auto *CSCallFunc = llvm::cast(Instr50); + const auto *CSSecondCallFunc = llvm::cast(Instr54); + + EXPECT_EQ((std::set{CallFunc->getArg(0)}), + getCallFlowValueSet(Instr50, AliasImpl, + CSCallFunc->getArgOperand(0), CallFunc)); + EXPECT_EQ((std::set{CallFunc->getArg(0)}), + getCallFlowValueSet(Instr50, NoAliasImpl, + CSCallFunc->getArgOperand(0), CallFunc)); + + EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(0), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(0), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(1), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(1), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), + getCallFlowValueSet(Instr54, AliasImpl, + CSSecondCallFunc->getArgOperand(2), + SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), + getCallFlowValueSet(Instr54, NoAliasImpl, + CSSecondCallFunc->getArgOperand(2), + SecondCallFunc)); +} + +/* + RetFlow +*/ + +TEST(PureFlow, RetFlow01) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Two = alloca i32, align 4, !psr.id !251; | ID: 20 + const auto *UnusedValue = IRDB.getValueFromId(20); + + // %call = call noundef i32 @_Z6getTwov(), !dbg !231, !psr.id !232; | ID: 9 + const auto *Instr9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instr9); + // ret i32 2, !dbg !212, !psr.id !213; | ID: 0 + const auto *Instr0 = IRDB.getInstruction(0); + ASSERT_TRUE(Instr0); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr9, AliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr9, NoAliasImpl, UnusedValue, Instr0)); + + // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg + // !281, !psr.id !282; | ID: 36 + const auto *Instr36 = IRDB.getInstruction(36); + ASSERT_TRUE(Instr36); + // ret i32 %add, !dbg !240, !psr.id !241; | ID: 14 + const auto *Instr14 = IRDB.getInstruction(14); + ASSERT_TRUE(Instr14); + const auto *FuncZ4callii = IRDB.getFunction("_Z4callii"); + + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, AliasImpl, FuncZ4callii->getArg(0), Instr14)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, AliasImpl, FuncZ4callii->getArg(1), Instr14)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr36, NoAliasImpl, FuncZ4callii->getArg(0), + Instr14)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr36, NoAliasImpl, FuncZ4callii->getArg(1), + Instr14)); + + // negative tests + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr9, AliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr9, NoAliasImpl, FuncZ4callii->getArg(1), Instr0)); +} + +TEST(PureFlow, RetFlow02) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %Two = alloca i32, align 4, !psr.id !268; | ID: 29 + const auto *UnusedValue = IRDB.getValueFromId(29); + + // %call = call noundef i32 @_Z6getTwov(), !dbg !240, !psr.id !241; | ID: 14 + const auto *Instr14 = IRDB.getInstruction(14); + ASSERT_TRUE(Instr14); + // ret i32 2, !dbg !212, !psr.id !213; | ID: 0 + const auto *Instr0 = IRDB.getInstruction(0); + ASSERT_TRUE(Instr0); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, AliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, NoAliasImpl, UnusedValue, Instr0)); + + // %call1 = call noundef i32 @_Z8newThreev(), !dbg !247, !psr.id !248; | ID: + // 18 + const auto *Instr18 = IRDB.getInstruction(18); + ASSERT_TRUE(Instr18); + // ret i32 3, !dbg !220, !psr.id !221; | ID: 4 + const auto *Instr4 = IRDB.getInstruction(4); + ASSERT_TRUE(Instr4); + + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr18, AliasImpl, UnusedValue, Instr4)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr18, NoAliasImpl, UnusedValue, Instr4)); + + // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg + // !298, !psr.id !299; | ID: 45 + const auto *Instr45 = IRDB.getInstruction(45); + ASSERT_TRUE(Instr45); + // ret i32 %add, !dbg !257, !psr.id !258; | ID: 23 + const auto *Instr23 = IRDB.getInstruction(23); + ASSERT_TRUE(Instr23); + const auto *FuncZ4callii = IRDB.getFunction("_Z4callii"); + + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, AliasImpl, FuncZ4callii->getArg(0), Instr23)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, AliasImpl, FuncZ4callii->getArg(1), Instr23)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr45, NoAliasImpl, FuncZ4callii->getArg(0), + Instr23)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr45, NoAliasImpl, FuncZ4callii->getArg(1), + Instr23)); + + // negative tests + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr14, AliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, NoAliasImpl, FuncZ4callii->getArg(1), + Instr0)); +} + +TEST(PureFlow, RetFlow03) { + LLVMProjectIRDB IRDB({unittest::PathToLLTestFiles + + "pure_flow/ret_flow/ret_flow_03_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // %ThreeInCall = alloca i32, align 4, !psr.id !254; | ID: 15 + const auto *Instr15 = IRDB.getValueFromId(15); + ASSERT_TRUE(Instr15); + + // %ThreePtrInCall = alloca ptr, align 8, !psr.id !255; | ID: 16 + const auto *Instr16 = IRDB.getValueFromId(16); + ASSERT_TRUE(Instr16); + + // %0 = load ptr, ptr %ThreePtrInCall, align 8, !dbg !280, !psr.id !281; | ID: + // 29 + const auto *Instr29 = IRDB.getValueFromId(29); + ASSERT_TRUE(Instr29); + + // %call = call noundef ptr @_Z8newThreePKi(ptr noundef %0), !dbg !282, + // !psr.id !283; | ID: 30 + const auto *Instr30 = IRDB.getValueFromId(30); + ASSERT_TRUE(Instr30); + + // ret ptr %1, !dbg !234, !psr.id !235; | ID: 9 + const auto *Instruction9 = IRDB.getInstruction(9); + ASSERT_TRUE(Instruction9); + // %call = call noundef ptr @_Z8newThreePKi(ptr noundef %0), !dbg !282, + // !psr.id !283; | ID: 30 + const auto *Instruction30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instruction30); + const auto *FunctionZ8newThreePKi = IRDB.getFunction("_Z8newThreePKi"); + + const auto &Got = getRetFlowValueSet( + Instruction30, AliasImpl, FunctionZ8newThreePKi->getArg(0), Instruction9); + + EXPECT_EQ((std::set{Instr15, Instr16, Instr29, Instr30}), + Got) + << stringifyValueSet(Got); + + EXPECT_EQ(std::set{Instr29}, + getRetFlowValueSet(Instruction30, NoAliasImpl, + FunctionZ8newThreePKi->getArg(0), Instruction9)); + + // ret ptr @GlobalFour, !dbg !240, !psr.id !241; | ID: 10 + const auto *Instruction10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instruction10); + // %call3 = call noundef ptr @_Z10getFourPtrv(), !dbg !304, !psr.id !305; | + // ID: 42 + const auto *Instruction42 = IRDB.getInstruction(42); + ASSERT_TRUE(Instruction42); + + // ret ptr @GlobalFour, !dbg !246, !psr.id !247; | ID: 11 + const auto *Instruction11 = IRDB.getInstruction(11); + ASSERT_TRUE(Instruction11); + // %call5 = call noundef nonnull align 4 dereferenceable(4) ptr + // @_Z11getFourAddrv(), !dbg !310, !psr.id !311; | ID: 45 + const auto *Instruction45 = IRDB.getInstruction(45); + ASSERT_TRUE(Instruction45); + + // ret i32 %add6, !dbg !315, !psr.id !316; | ID: 48 + const auto *Instruction48 = IRDB.getInstruction(48); + ASSERT_TRUE(Instruction48); + // %call = call noundef i32 @_Z4callRiPKi(ptr noundef nonnull align 4 + // dereferenceable(4) %Zero, ptr noundef %One), !dbg !352, !psr.id !353; | ID: + // 68 + const auto *Instruction68 = IRDB.getInstruction(68); + ASSERT_TRUE(Instruction68); + const auto *FuncZ4callRiPKi = IRDB.getFunction("_Z4callRiPKi"); + // %Zero = alloca i32, align 4, !psr.id !324; | ID: 52 + const auto *Instr52 = IRDB.getInstruction(52); + ASSERT_TRUE(Instr52); + + // %One = alloca i32, align 4, !psr.id !325; | ID: 53 + const auto *Instr53 = IRDB.getInstruction(53); + ASSERT_TRUE(Instr53); + + EXPECT_EQ(std::set{Instr52}, + getRetFlowValueSet(Instruction68, AliasImpl, + FuncZ4callRiPKi->getArg(0), Instruction48)); + EXPECT_EQ(std::set{Instr52}, + getRetFlowValueSet(Instruction68, NoAliasImpl, + FuncZ4callRiPKi->getArg(0), Instruction48)); + EXPECT_EQ(std::set{Instr53}, + getRetFlowValueSet(Instruction68, AliasImpl, + FuncZ4callRiPKi->getArg(1), Instruction48)); + EXPECT_EQ(std::set{Instr53}, + getRetFlowValueSet(Instruction68, NoAliasImpl, + FuncZ4callRiPKi->getArg(1), Instruction48)); +} + +/* + CallToRetFlow +*/ + +TEST(PureFlow, CallToRetFlow01) { + LLVMProjectIRDB IRDB( + {unittest::PathToLLTestFiles + + "pure_flow/call_to_ret_flow/call_to_ret_flow_01_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // store i32 0, ptr %Zero, align 4, !dbg !252, !psr.id !254; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + + // store i32 1, ptr %One, align 4, !dbg !256, !psr.id !258; | ID: 24 + const auto *Instr24 = IRDB.getInstruction(24); + ASSERT_TRUE(Instr24); + + // ret i32 0, !dbg !269, !psr.id !270; | ID: 30 + const auto *Instr30 = IRDB.getInstruction(30); + ASSERT_TRUE(Instr30); + + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr22)); + + EXPECT_EQ(std::set{Instr24}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr24)); + EXPECT_EQ(std::set{Instr24}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr24)); + + EXPECT_EQ(std::set{Instr30}, + getCallToRetFlowValueSet(Instr30, AliasImpl, Instr30)); + EXPECT_EQ(std::set{Instr30}, + getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr30)); +} + +TEST(PureFlow, CallToRetFlow02) { + LLVMProjectIRDB IRDB( + {unittest::PathToLLTestFiles + + "pure_flow/call_to_ret_flow/call_to_ret_flow_02_cpp_dbg.ll"}); + IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); + IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + + // store i32 3, ptr %Three, align 4, !dbg !223, !psr.id !225; | ID: 5 + const auto *Instr5 = IRDB.getInstruction(5); + ASSERT_TRUE(Instr5); + + // store i32 4, ptr %Four, align 4, !dbg !229, !psr.id !231; | ID: 8 + const auto *Instr8 = IRDB.getInstruction(8); + ASSERT_TRUE(Instr8); + + // ret void, !dbg !234, !psr.id !235; | ID: 10 + const auto *Instr10 = IRDB.getInstruction(10); + ASSERT_TRUE(Instr10); + + EXPECT_EQ(std::set{Instr5}, + getCallToRetFlowValueSet(Instr10, AliasImpl, Instr5)); + EXPECT_EQ(std::set{Instr5}, + getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr5)); + + EXPECT_EQ(std::set{Instr8}, + getCallToRetFlowValueSet(Instr10, AliasImpl, Instr8)); + EXPECT_EQ(std::set{Instr8}, + getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr8)); + + // store i32 1, ptr %One, align 4, !dbg !255, !psr.id !257; | ID: 22 + const auto *Instr22 = IRDB.getInstruction(22); + ASSERT_TRUE(Instr22); + + // store i32 2, ptr %Two, align 4, !dbg !261, !psr.id !263; | ID: 25 + const auto *Instr25 = IRDB.getInstruction(25); + ASSERT_TRUE(Instr25); + + // ret i32 0, !dbg !266, !psr.id !267; | ID: 27 + const auto *Instr27 = IRDB.getInstruction(27); + ASSERT_TRUE(Instr27); + + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr22)); + + EXPECT_EQ(std::set{Instr25}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr25)); + EXPECT_EQ(std::set{Instr25}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr25)); + + EXPECT_EQ(std::set{Instr27}, + getCallToRetFlowValueSet(Instr27, AliasImpl, Instr27)); + EXPECT_EQ(std::set{Instr27}, + getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr27)); +} + +}; // namespace + +// main function for the test case +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +} From 28a25b712d0118ed57fae7c82905879fb23d85dc Mon Sep 17 00:00:00 2001 From: jusito <17811819+jusito@users.noreply.github.com> Date: Fri, 4 Apr 2025 18:48:04 +0200 Subject: [PATCH 06/24] ci/update runner (#769) * ci: Prepare runner brownout Swift tests are disabled due to dependency on outdated Swift version. * fix: tests on ubuntu 24.04 * fix: update clang-format in pre-commit hook to match clang-format version in CI * pre-commit --------- Co-authored-by: Lucas Briese Co-authored-by: Fabian Schiebel --- .github/workflows/ci.yml | 23 +------------------ .github/workflows/pre-commit.yml | 8 +++---- .github/workflows/reviewdog-clang-format.yml | 10 ++++---- .pre-commit-config.yaml | 2 +- .../ControlFlow/LLVMBasedCFGTest.cpp | 7 +----- .../Problems/IDEExtendedTaintAnalysisTest.cpp | 2 +- .../Problems/IDEFeatureTaintAnalysisTest.cpp | 23 +++++++++---------- .../Problems/IDEGeneralizedLCATest.cpp | 2 +- .../IDEInstInteractionAnalysisTest.cpp | 12 +++++----- 9 files changed, 30 insertions(+), 59 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 30b1cd00c2..a68d15bcdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-20.04, ubuntu-24.04-arm] + os: [ubuntu-24.04, ubuntu-24.04-arm] compiler: [ [clang++-19, clang-19, "clang-19 libclang-rt-19-dev"] ] build: [ Debug, Release, DebugLibdeps ] include: @@ -46,28 +46,7 @@ jobs: run: | ./utils/InstallAptDependencies.sh --noninteractive tzdata ${{ matrix.compiler[2] }} - - uses: swift-actions/setup-swift@v2 - if: matrix.os == 'ubuntu-20.04' - with: - swift-version: "5.8.1" - - name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }} including swift - if: matrix.os == 'ubuntu-20.04' - env: - CXX: ${{ matrix.compiler[0] }} - CC: ${{ matrix.compiler[1] }} - shell: bash - run: | - cmake -S . -B build \ - -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ - -DBUILD_PHASAR_CLANG=OFF \ - -DBUILD_SWIFT_TESTS=ON \ - -DPHASAR_USE_Z3=ON \ - ${{ matrix.flags }} \ - -G Ninja - ninja -C build - - name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }} - if: matrix.os != 'ubuntu-20.04' env: CXX: ${{ matrix.compiler[0] }} CC: ${{ matrix.compiler[1] }} diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 86df3a0ad8..bce9eb49b1 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -5,14 +5,14 @@ on: jobs: pre-commit: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 - - uses: actions/setup-python@v2 + - uses: actions/setup-python@v5 - - uses: pre-commit/action@v2.0.0 + - uses: pre-commit/action@v3.0.1 with: extra_args: --from-ref origin/development --to-ref HEAD diff --git a/.github/workflows/reviewdog-clang-format.yml b/.github/workflows/reviewdog-clang-format.yml index d3eed65d9a..b6b5bacb91 100644 --- a/.github/workflows/reviewdog-clang-format.yml +++ b/.github/workflows/reviewdog-clang-format.yml @@ -6,7 +6,7 @@ on: jobs: format: - runs-on: ubuntu-20.04 + runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: @@ -14,17 +14,15 @@ jobs: include: - tool: clang-format install: | - sudo apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key - sudo add-apt-repository -y 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main' sudo apt-get update - sudo apt-get -y install --no-install-recommends clang-format-14 + sudo apt-get -y install --no-install-recommends clang-format-19 regex: \.(h|c|hpp|cpp)$ - command: clang-format-14 --style=file -i + command: clang-format-19 --style=file -i continue-on-error: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 786e0aba36..5543f809cf 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -12,6 +12,6 @@ repos: - id: check-added-large-files - id: requirements-txt-fixer - repo: https://github.com/pre-commit/mirrors-clang-format - rev: 'v14.0.6' + rev: 'v19.1.7' hooks: - id: clang-format diff --git a/unittests/PhasarLLVM/ControlFlow/LLVMBasedCFGTest.cpp b/unittests/PhasarLLVM/ControlFlow/LLVMBasedCFGTest.cpp index 3753a403a2..546306de0e 100644 --- a/unittests/PhasarLLVM/ControlFlow/LLVMBasedCFGTest.cpp +++ b/unittests/PhasarLLVM/ControlFlow/LLVMBasedCFGTest.cpp @@ -245,13 +245,8 @@ PHASAR_SKIP_TEST(TEST(LLVMBasedCFGTest, HandlesCppStandardType) { {unittest::PathToLLTestFiles + "name_mangling/special_members_2_cpp.ll"}); auto *M = IRDB.getModule(); - auto *F = M->getFunction("_ZNSt8ios_base4InitC1Ev"); + LLVMBasedCFG CFG; - ASSERT_EQ(CFG.getSpecialMemberFunctionType(F), - SpecialMemberFunctionType::Constructor); - auto *N = M->getFunction("_ZNSt8ios_base4InitD1Ev"); - ASSERT_EQ(CFG.getSpecialMemberFunctionType(N), - SpecialMemberFunctionType::Destructor); auto *O = M->getFunction( "_ZNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEED1Ev"); ASSERT_EQ(CFG.getSpecialMemberFunctionType(O), diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysisTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysisTest.cpp index 5c3395b6d2..51af9f3058 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysisTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysisTest.cpp @@ -208,7 +208,7 @@ TEST_F(IDETaintAnalysisTest, XTaint09_1) { TEST_F(IDETaintAnalysisTest, XTaint09) { map> Gt; - Gt[33] = {"32"}; + Gt[24] = {"23"}; doAnalysis({PathToLLFiles + "xtaint09_cpp.ll"}, Gt, std::monostate{}); } diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp index cf0d4ac181..243cb2d6a7 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp @@ -152,8 +152,7 @@ class IDEFeatureTaintAnalysisTest : public ::testing::Test { [[nodiscard]] inline std::set taintsForInst(const llvm::Instruction *Inst, SolverResults - SR) { + IDEFeatureTaintEdgeFact> SR) { if (const auto *Ret = llvm::dyn_cast(Inst)) { if (Ret->getNumOperands() == 0) { @@ -243,15 +242,15 @@ PHASAR_SKIP_TEST(TEST_F(IDEFeatureTaintAnalysisTest, HandleBasicTest_04) { LIBCPP_GTEST_SKIP; std::set GroundTruth; - GroundTruth.emplace("main", 23, "retval", std::set{"13"}); - GroundTruth.emplace("main", 23, "argc.addr", std::set{"14"}); - GroundTruth.emplace("main", 23, "argv.addr", std::set{"15"}); - GroundTruth.emplace("main", 23, "i", std::set{"16"}); + GroundTruth.emplace("main", 23, "retval", std::set{"7"}); + GroundTruth.emplace("main", 23, "argc.addr", std::set{"8"}); + GroundTruth.emplace("main", 23, "argv.addr", std::set{"9"}); + GroundTruth.emplace("main", 23, "i", std::set{"10"}); GroundTruth.emplace("main", 23, "j", - std::set{"16", "17", "19", "18"}); + std::set{"10", "11", "12", "13"}); GroundTruth.emplace( "main", 23, "k", - std::set{"16", "17", "18", "19", "20", "24", "25"}); + std::set{"10", "11", "12", "13", "14", "18", "19"}); doAnalysisAndCompareResults("basic_04_cpp.ll", {"main"}, GroundTruth, false); }) @@ -444,11 +443,11 @@ TEST_F(IDEFeatureTaintAnalysisTest, HandleGlobalTest_05) { // NOTE: Facts at init() should be empty, except for its own ID; // g should be strongly updated - GroundTruth.emplace("main", 1, "g", std::set{"2"}); - GroundTruth.emplace("main", 2, "g", std::set{"8"}); + GroundTruth.emplace("main", 1, "g", std::set{"0"}); + GroundTruth.emplace("main", 2, "g", std::set{"2"}); GroundTruth.emplace("main", 4, "call", - std::set{"10", "13", "14", "8"}); - GroundTruth.emplace("main", 4, "g", std::set{"8"}); + std::set{"2", "4", "7", "8"}); + GroundTruth.emplace("main", 4, "g", std::set{"2"}); doAnalysisAndCompareResults("global_05_cpp.ll", {"main"}, GroundTruth, true); } diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCATest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCATest.cpp index 03165a8abb..d81885c43f 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCATest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCATest.cpp @@ -130,7 +130,7 @@ TEST_F(IDEGeneralizedLCATest, StringTestCpp) { const auto *LastMainInstruction = getLastInstructionOf(HA->getProjectIRDB().getFunction("main")); GroundTruth.push_back({{EdgeValue("Hello, World")}, - 3, + 7, static_cast( std::stoi(getMetaDataID(LastMainInstruction)))}); compareResults(GroundTruth); diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysisTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysisTest.cpp index a1368dc258..59a474013b 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysisTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysisTest.cpp @@ -482,22 +482,22 @@ PHASAR_SKIP_TEST(TEST_F(IDEInstInteractionAnalysisTest, HandleBasicTest_04) { std::set GroundTruth; GroundTruth.emplace( std::tuple>( - "main", 23, "retval", {"13"})); + "main", 23, "retval", {"7"})); GroundTruth.emplace( std::tuple>( - "main", 23, "argc.addr", {"14"})); + "main", 23, "argc.addr", {"8"})); GroundTruth.emplace( std::tuple>( - "main", 23, "argv.addr", {"15"})); + "main", 23, "argv.addr", {"9"})); GroundTruth.emplace( std::tuple>( - "main", 23, "i", {"16"})); + "main", 23, "i", {"10"})); GroundTruth.emplace( std::tuple>( - "main", 23, "j", {"16", "17", "19", "18"})); + "main", 23, "j", {"10", "11", "12", "13"})); GroundTruth.emplace( std::tuple>( - "main", 23, "k", {"16", "17", "18", "19", "20", "24", "25"})); + "main", 23, "k", {"10", "11", "12", "13", "14", "18", "19"})); doAnalysisAndCompareResults("basic_04_cpp.ll", {"main"}, GroundTruth, false); }) From 64412c57ec328fb360c37e56a14b022052d6c0be Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 19 May 2025 18:37:28 +0200 Subject: [PATCH 07/24] SVF Pointer Analyses (#767) * Add SVF dependency + make the LLVMBasedAliasAnalysis polymorphic to allow injecting the SVF analysis * Add public interface for SVFPointsToSet * Add SVFPointsToInfo + Fix PointsToInfoBase * Fixes in PointsToInfo + add minimal tests * Add CRTPBase utility * New test + some cleanup * Fix build due to merge * minor * Simplify the SVF alias analyses + make FunctionAliasView more type-safe * minor * Regain precision * Get rid of UB + aim to resolve memory leaks with SVF * Fix SVF memory leak * pre-commit with new clang-format version * Apply review comments * Getting rid of the PSR_BIND_ALIASVIEW macro * fix compilation --- .clang-tidy | 2 + .gitignore | 5 +- CMakeLists.txt | 12 ++ config.h.in | 2 + include/phasar/ControlFlow/CFGBase.h | 21 +-- include/phasar/ControlFlow/CallGraphBase.h | 11 +- include/phasar/ControlFlow/ICFGBase.h | 23 ++- .../PhasarLLVM/ControlFlow/LLVMBasedCFG.h | 1 - include/phasar/PhasarLLVM/Pointer.h | 2 +- .../PhasarLLVM/Pointer/AliasAnalysisView.h | 99 ++++++++++++ .../phasar/PhasarLLVM/Pointer/LLVMAliasSet.h | 8 +- .../PhasarLLVM/Pointer/SVF/SVFPointsToSet.h | 55 +++++++ include/phasar/Pointer/AliasAnalysisType.def | 2 + include/phasar/Pointer/PointsToInfo.h | 33 ++-- include/phasar/Pointer/PointsToInfoBase.h | 44 ++--- include/phasar/Utils/CRTPUtils.h | 32 ++++ include/phasar/Utils/Fn.h | 43 +++++ include/phasar/Utils/PointerUtils.h | 27 ++++ lib/CMakeLists.txt | 3 + lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp | 34 ++++ lib/PhasarLLVM/Pointer/CMakeLists.txt | 9 +- .../Pointer/FilteredLLVMAliasSet.cpp | 2 + lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp | 61 ++----- .../Pointer/LLVMBasedAliasAnalysis.cpp | 91 +++++++++-- .../Pointer/LLVMBasedAliasAnalysis.h | 45 +++--- lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt | 24 +++ lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp | 46 ++++++ lib/PhasarLLVM/Pointer/SVF/InitSVF.h | 15 ++ .../Pointer/SVF/SVFBasedAliasAnalysis.cpp | 123 ++++++++++++++ .../Pointer/SVF/SVFBasedAliasAnalysis.h | 25 +++ lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp | 153 ++++++++++++++++++ .../Pointer/external/CMakeLists.txt | 2 + lib/Pointer/PointsToInfo.cpp | 19 +-- unittests/PhasarLLVM/Pointer/CMakeLists.txt | 12 +- .../PhasarLLVM/Pointer/SVFAliasSetTest.cpp | 92 +++++++++++ 35 files changed, 978 insertions(+), 200 deletions(-) create mode 100644 include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h create mode 100644 include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h create mode 100644 include/phasar/Utils/CRTPUtils.h create mode 100644 include/phasar/Utils/Fn.h create mode 100644 lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp rename {include/phasar => lib}/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h (55%) create mode 100644 lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt create mode 100644 lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp create mode 100644 lib/PhasarLLVM/Pointer/SVF/InitSVF.h create mode 100644 lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp create mode 100644 lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h create mode 100644 lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp create mode 100644 lib/PhasarLLVM/Pointer/external/CMakeLists.txt create mode 100644 unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp diff --git a/.clang-tidy b/.clang-tidy index 94ed69701b..3e95f2eaea 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -29,10 +29,12 @@ Checks: '-*, -cppcoreguidelines-init-variables, -cppcoreguidelines-macro-usage, -cppcoreguidelines-avoid-do-while, + -cppcoreguidelines-avoid-c-arrays, bugprone-*, -bugprone-easily-swappable-parameters, modernize-*, -modernize-use-trailing-return-type, + -modernize-avoid-c-arrays, performance-*, clang-analyzer-* ' diff --git a/.gitignore b/.gitignore index f86934e8b1..2aaf8ce5f8 100644 --- a/.gitignore +++ b/.gitignore @@ -30,11 +30,8 @@ doc/* log/* **/*/logs/ -# CMake build dir -build/* - # MS VS Code -.vscode/* +.vscode/ # Eclipse .cproject diff --git a/CMakeLists.txt b/CMakeLists.txt index 72e0a73b99..5370c88ee3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -308,6 +308,18 @@ endif() include(add_llvm) add_llvm() +# SVF +option(PHASAR_USE_SVF "Use SVF for more options in alias analysis (default is OFF)" OFF) +if(PHASAR_USE_SVF) + find_package(SVF REQUIRED CONFIG) + message(STATUS "Found SVF ${SVF_VERSION}") + + if (NOT PHASAR_USE_Z3) + message(WARNING "SVF requires Z3. Set PHASAR_USE_Z3=ON") + set(PHASAR_USE_Z3 ON) + endif() +endif() + # Z3 Solver if(PHASAR_IN_TREE) set (PHASAR_USE_Z3 OFF) diff --git a/config.h.in b/config.h.in index 97b0e8a9f6..926d69b0bf 100644 --- a/config.h.in +++ b/config.h.in @@ -13,4 +13,6 @@ #cmakedefine PHASAR_HAS_SQLITE +#cmakedefine PHASAR_USE_SVF + #endif /* PHASAR_CONFIG_CONFIG_H */ diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index 6f0f1d610d..5b932f2b5a 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -11,6 +11,7 @@ #define PHASAR_CONTROLFLOW_CFGBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" namespace psr { @@ -22,7 +23,12 @@ template struct CFGTraits { // using f_t }; -template class CFGBase { +template class CFGBase : public CRTPBase { + friend Derived; + +protected: + using CRTPBase::self; + public: using n_t = typename CFGTraits::n_t; using f_t = typename CFGTraits::f_t; @@ -129,19 +135,14 @@ template class CFGBase { void print(ByConstRef Fun, llvm::raw_ostream &OS) const { self().printImpl(Fun, OS); } - -protected: - Derived &self() noexcept { return static_cast(*this); } - const Derived &self() const noexcept { - return static_cast(*this); - } }; template // NOLINTNEXTLINE(readability-identifier-naming) -PSR_CONCEPT is_cfg_v = is_crtp_base_of_v - &&std::is_same_v - &&std::is_same_v; +PSR_CONCEPT is_cfg_v = + is_crtp_base_of_v && + std::is_same_v && + std::is_same_v; } // namespace psr diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h index 2f2371618b..67bfb0cc94 100644 --- a/include/phasar/ControlFlow/CallGraphBase.h +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -11,6 +11,7 @@ #define PHASAR_CONTROLFLOW_CALLGRAPHBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" namespace psr { @@ -22,7 +23,10 @@ template struct CGTraits { /// Base class of all CallGraph implementations within phasar (currently only /// CallGraph). /// Only represents the data, not how to create it. -template class CallGraphBase { +template class CallGraphBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using n_t = typename CGTraits::n_t; using f_t = typename CGTraits::f_t; @@ -46,11 +50,6 @@ template class CallGraphBase { is_iterable_over_v); return self().getCallersOfImpl(Fun); } - -private: - const Derived &self() const noexcept { - return static_cast(*this); - } }; } // namespace psr diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index 4ef63126d1..71f340462c 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -12,6 +12,7 @@ #include "phasar/ControlFlow/CFGBase.h" #include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/Utils/CRTPUtils.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" @@ -20,16 +21,14 @@ #include namespace psr { -template class ICFGBase { +template class ICFGBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using n_t = typename CFGTraits::n_t; using f_t = typename CFGTraits::f_t; - ICFGBase() noexcept { - static_assert(is_crtp_base_of_v, - "An ICFG must also be a CFG"); - } - /// Returns an iterable range of all function definitions or declarations in /// the ICFG [[nodiscard]] decltype(auto) getAllFunctions() const { @@ -113,20 +112,16 @@ template class ICFGBase { [[nodiscard]] size_t getNumCallSites() const noexcept { return self().getNumCallSitesImpl(); } - -private: - const Derived &self() const noexcept { - return static_cast(*this); - } }; /// True, iff ICF is a proper instantiation of ICFGBase with n_t and f_t taken /// from the given analysis-Domain template // NOLINTNEXTLINE(readability-identifier-naming) -PSR_CONCEPT is_icfg_v = is_crtp_base_of_v - &&std::is_same_v - &&std::is_same_v; +PSR_CONCEPT is_icfg_v = + is_crtp_base_of_v && + std::is_same_v && + std::is_same_v; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h index a3134c7131..9eee1395d6 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h @@ -38,7 +38,6 @@ template <> struct CFGTraits : CFGTraits {}; namespace detail { template class LLVMBasedCFGImpl : public CFGBase { - friend CFGBase; friend class LLVMBasedBackwardCFG; public: diff --git a/include/phasar/PhasarLLVM/Pointer.h b/include/phasar/PhasarLLVM/Pointer.h index a2a67fd202..8025df0f4d 100644 --- a/include/phasar/PhasarLLVM/Pointer.h +++ b/include/phasar/PhasarLLVM/Pointer.h @@ -10,10 +10,10 @@ #ifndef PHASAR_PHASARLLVM_POINTER_H #define PHASAR_PHASARLLVM_POINTER_H +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #endif // PHASAR_PHASARLLVM_POINTER_H diff --git a/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h b/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h new file mode 100644 index 0000000000..afdedb5de6 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/AliasAnalysisView.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_POINTER_ALIASANALYSISVIEW_H +#define PHASAR_PHASARLLVM_POINTER_ALIASANALYSISVIEW_H + +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" + +#include +#include + +namespace llvm { +class Value; +class DataLayout; +class Function; +} // namespace llvm + +namespace psr { +class LLVMProjectIRDB; + +class FunctionAliasView { +public: + template + using AliasCallbackTy = AliasResult (*)(T *, const llvm::Value *, + const llvm::Value *, + const llvm::DataLayout &); + + [[nodiscard]] AliasResult alias(const llvm::Value *V, const llvm::Value *Rep, + const llvm::DataLayout &DL) { + return Alias(Context, V, Rep, DL); + } + + template < + typename T, typename AliasFn, + typename = std::enable_if_t && + std::is_default_constructible_v>> + constexpr FunctionAliasView(T *Context, AliasFn /*Alias*/) noexcept + : Context(Context), Alias(&callAlias) {} + +private: + template + static AliasResult callAlias(void *Context, const llvm::Value *V1, + const llvm::Value *V2, + const llvm::DataLayout &DL) { + return AliasFn{}(static_cast(Context), V1, V2, DL); + } + + void *Context{}; + AliasCallbackTy Alias{}; +}; + +class AliasAnalysisView { +public: + constexpr AliasAnalysisView(AliasAnalysisType PATy) noexcept : PATy(PATy) {} + + virtual ~AliasAnalysisView() = default; + + [[nodiscard]] FunctionAliasView getAAResults(const llvm::Function *F) { + assert(F != nullptr); + return doGetAAResults(F); + } + + void erase(llvm::Function *F) noexcept { + assert(F != nullptr); + doErase(F); + } + + void clear() noexcept { doClear(); } + + [[nodiscard]] constexpr AliasAnalysisType + getPointerAnalysisType() const noexcept { + return PATy; + }; + + [[nodiscard]] static std::unique_ptr + create(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy); + +private: + static std::unique_ptr + createLLVMBasedAnalysis(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, + AliasAnalysisType PATy); + + virtual FunctionAliasView doGetAAResults(const llvm::Function *F) = 0; + virtual void doErase(llvm::Function *F) noexcept = 0; + virtual void doClear() noexcept = 0; + + AliasAnalysisType PATy{}; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_ALIASANALYSISVIEW_H diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index ff4ccbe5dc..1c77b6f0cd 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -10,8 +10,8 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H #define PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" @@ -69,7 +69,7 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, }; [[nodiscard]] inline AliasAnalysisType getAliasAnalysisType() const noexcept { - return PTA.getPointerAnalysisType(); + return PTA->getPointerAnalysisType(); }; [[nodiscard]] AliasResult alias(const llvm::Value *V1, const llvm::Value *V2, @@ -143,12 +143,12 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, const llvm::GlobalObject *VG) const; /// Utility function used by computeFunctionsAliasSet(...) - void addPointer(llvm::AAResults &AA, const llvm::DataLayout &DL, + void addPointer(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, std::vector &Reps); [[nodiscard]] static BoxedPtr getEmptyAliasSet(); - LLVMBasedAliasAnalysis PTA; + std::unique_ptr PTA; llvm::DenseSet AnalyzedFunctions; AliasSetOwner::memory_resource_type MRes; diff --git a/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h new file mode 100644 index 0000000000..55c753222b --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ +#ifndef PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H +#define PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H + +#include "phasar/Config/phasar-config.h" +#include "phasar/Pointer/PointsToInfo.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/IR/Value.h" + +#ifndef PHASAR_USE_SVF +#error \ + "Don't include SVFPointsToSet.h when PhASAR is not configured to include SVF. Set the cmake variable PHASAR_USE_SVF and retry." +#endif + +namespace psr { +class LLVMProjectIRDB; +class SVFPointsToInfo; + +struct SVFPointsToInfoTraits { + using v_t = const llvm::Value *; + using n_t = const llvm::Instruction *; + using o_t = uint32_t; + + // TODO: Use a more efficient representation; maybe even one that does not + // require an expensive transformation from SVF::PointsTo + using PointsToSetTy = llvm::SmallDenseSet; + + // No special pointer type + using PointsToSetPtrTy = PointsToSetTy; +}; + +using SVFBasedPointsToInfo = PointsToInfo; +using SVFBasedPointsToInfoRef = PointsToInfoRef; + +/// Use SVF to perform a VersionedFlowSensitive pointer analysis and return the +/// results compatible to psr::PointsToInfo and psr::PointsToInfoRef +[[nodiscard]] SVFBasedPointsToInfo +createSVFVFSPointsToInfo(LLVMProjectIRDB &IRDB); + +/// Use SVF to perform a ContextDDA pointer analysis and return the +/// results compatible to psr::PointsToInfo and psr::PointsToInfoRef +[[nodiscard]] SVFBasedPointsToInfo +createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB); + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_SVF_SVFPOINTSTOSET_H diff --git a/include/phasar/Pointer/AliasAnalysisType.def b/include/phasar/Pointer/AliasAnalysisType.def index 93c3599d77..dda81808ca 100644 --- a/include/phasar/Pointer/AliasAnalysisType.def +++ b/include/phasar/Pointer/AliasAnalysisType.def @@ -15,5 +15,7 @@ ALIAS_ANALYSIS_TYPE(Basic, "basic", "Basic LLVM alias resolving based on simple, ALIAS_ANALYSIS_TYPE(CFLSteens, "cflsteens", "Steensgaard-style alias analysis (equality-based)") ALIAS_ANALYSIS_TYPE(CFLAnders, "cflanders", "Andersen-style alias analysis (subset-based) (default)") ALIAS_ANALYSIS_TYPE(PointsTo, "points-to", "Alias-information based on (external) points-to information") +ALIAS_ANALYSIS_TYPE(SVFDDA, "svf-dda", "Alias-information based on SVF's ContextDDA analysis. Requires SVF.") +ALIAS_ANALYSIS_TYPE(SVFVFS, "svf-vfs", "Alias-information based on SVF's VersionedFlowSensitive analysis. Requires SVF.") #undef ALIAS_ANALYSIS_TYPE diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index 2eaf0f4387..af2f54d587 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -14,10 +14,10 @@ #include "phasar/Utils/ByRef.h" #include +#include #include #include #include -#include namespace psr { @@ -29,7 +29,7 @@ struct PointsToTraits> : PTATraits {}; template struct PointsToTraits> : PTATraits {}; -/// A type-erased reference to any object implementing th PointsToInfoBase +/// A type-erased reference to any object implementing the PointsToInfoBase /// interface. Use this, if your analysis is not tied to a specific points-to /// info implementation. /// @@ -87,7 +87,6 @@ class PointsToInfoRef, ByConstRef); - std::vector (*GetInterestingPointersAt)(const void *, ByConstRef); void (*Destroy)(const void *) noexcept; // Useful for the owning variant }; @@ -120,15 +119,6 @@ class PointsToInfoRef(PT)->getPointsToSet( Pointer, AtInstruction); }, - [](const void *PT, ByConstRef AtInstruction) { - std::vector Ret; - for (ByConstRef Ptr : - static_cast(PT)->getInterestingPointersAt( - AtInstruction)) { - Ret.push_back(Ptr); - } - return Ret; - }, [](const void *PT) noexcept { delete static_cast(PT); }, @@ -164,7 +154,7 @@ class PointsToInfoRef - asPointerOrNull(ByConstRef Obj) const noexcept { + asPointerOrNullImpl(ByConstRef Obj) const noexcept { assert(VT); return VT->AsPointerOrNull(PT, Obj); } @@ -201,18 +191,12 @@ class PointsToInfoRefGetPointsToSetV(PT, Pointer, AtInstruction); } - std::vector - getInterestingPointersAtImpl(ByConstRef AtInstruction) const { - assert(VT); - return VT->GetInterestingPointersAt(PT, AtInstruction); - } - // --- const void *PT{}; const VTable<> *VT{}; }; -/// Similar to PointsToInfoRef, but owns the held reference. Us this, if you +/// Similar to PointsToInfoRef, but owns the held reference. Use this, if you /// need to decide dynamically, which points-to info implementation to use. /// /// Implicitly convertible to PointsToInfoRef. @@ -232,12 +216,13 @@ class [[clang::trivial_abi]] PointsToInfo< PointsToInfo() noexcept = default; PointsToInfo(std::nullptr_t) noexcept {}; + PointsToInfo(const PointsToInfo &) = delete; PointsToInfo &operator=(const PointsToInfo &) = delete; + PointsToInfo(PointsToInfo &&Other) noexcept { swap(Other); } PointsToInfo &operator=(PointsToInfo &&Other) noexcept { - auto Cpy{std::move(Other)}; - swap(Cpy); + PointsToInfo(std::move(Other)).swap(*this); return *this; } @@ -255,6 +240,10 @@ class [[clang::trivial_abi]] PointsToInfo< : PointsToInfoRef( new ConcretePTA(std::forward(Args)...)) {} + template + PointsToInfo(std::unique_ptr PTA) + : PointsToInfoRef(PTA.release()) {} + ~PointsToInfo() noexcept { if (*this) { this->VT->Destroy(this->PT); diff --git a/include/phasar/Pointer/PointsToInfoBase.h b/include/phasar/Pointer/PointsToInfoBase.h index 9eef7cf3ad..0bb22091fb 100644 --- a/include/phasar/Pointer/PointsToInfoBase.h +++ b/include/phasar/Pointer/PointsToInfoBase.h @@ -11,6 +11,8 @@ #define PHASAR_POINTER_POINTSTOINFOBASE_H #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/CRTPUtils.h" +#include "phasar/Utils/PointerUtils.h" #include "phasar/Utils/TypeTraits.h" #include @@ -52,7 +54,10 @@ PSR_CONCEPT is_equivalent_PointsToTraits_v = // NOLINT /// Base class of all points-to analysis implementations. Don't use this class /// directly. For a type-erased variant, use PointsToInfoRef or PointsToInfo. -template class PointsToInfoBase { +template class PointsToInfoBase : public CRTPBase { + friend Derived; + using CRTPBase::self; + public: using v_t = typename PointsToTraits::v_t; using n_t = typename PointsToTraits::n_t; @@ -60,12 +65,6 @@ template class PointsToInfoBase { using PointsToSetTy = typename PointsToTraits::PointsToSetTy; using PointsToSetPtrTy = typename PointsToTraits::PointsToSetPtrTy; - explicit PointsToInfoBase() noexcept { - static_assert(std::is_base_of_v, - "Invalid CRTP instantiation: Derived must inherit from " - "PointsToInfoBase!"); - } - /// Creates an abstract object corresponding to the given pointer [[nodiscard]] o_t asAbstractObject(ByConstRef Pointer) const noexcept { return self().asAbstractObjectImpl(Pointer); @@ -102,29 +101,21 @@ template class PointsToInfoBase { return self().getPointsToSetImpl(Pointer, AtInstruction); } - /// Gets all pointers v_t where we have non-empty points-to information at - /// this instruction - [[nodiscard]] decltype(auto) - getInterestingPointersAt(ByConstRef AtInstruction) const { - static_assert( - is_iterable_over_v< - decltype(self().getInterestingPointersAtImpl(AtInstruction)), v_t>); - return self().getInterestingPointersAtImpl(AtInstruction); - } - private: - template >> - [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, + [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, ByConstRef Obj, ByConstRef AtInstruction) const { - return getPointsToSet(asAbstractObject(Pointer), AtInstruction)->count(Obj); + auto &&Pts = getPointsToSet(Pointer, AtInstruction); + return getPointerFrom(Pts)->count(Obj); } - [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, + template >> + [[nodiscard]] bool mayPointsToImpl(ByConstRef Pointer, ByConstRef Obj, ByConstRef AtInstruction) const { - return getPointsToSet(Pointer, AtInstruction)->count(Obj); + return self().mayPointsTo(self().asAbstractObject(Pointer), Obj, + AtInstruction); } template class PointsToInfoBase { ByConstRef AtInstruction) const { return self().getPointsToSetImpl(asAbstractObject(Pointer), AtInstruction); } - - [[nodiscard]] Derived &self() noexcept { - return static_cast(*this); - } - [[nodiscard]] const Derived &self() const noexcept { - return static_cast(*this); - } }; } // namespace psr diff --git a/include/phasar/Utils/CRTPUtils.h b/include/phasar/Utils/CRTPUtils.h new file mode 100644 index 0000000000..e66e394e00 --- /dev/null +++ b/include/phasar/Utils/CRTPUtils.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_CRTPUTILS_H +#define PHASAR_UTILS_CRTPUTILS_H + +namespace psr { +template class CRTPBase { + friend Derived; + +protected: + constexpr CRTPBase() noexcept = default; + + [[nodiscard]] constexpr Derived &self() & noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr Derived &&self() && noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr const Derived &self() const & noexcept { + return static_cast(*this); + } +}; +} // namespace psr + +#endif // PHASAR_UTILS_CRTPUTILS_H diff --git a/include/phasar/Utils/Fn.h b/include/phasar/Utils/Fn.h new file mode 100644 index 0000000000..55cc644149 --- /dev/null +++ b/include/phasar/Utils/Fn.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_FN_H +#define PHASAR_UTILS_FN_H + +#include "phasar/Utils/Macros.h" + +#include +#include + +namespace psr { +/// \brief A helper that transforms a statically known function pointer into a +/// type. +/// +/// Useful for passing functions as callbacks, while avoiding either the +/// indirect call overhead or wrapping a lambda around +template struct fn_t { // NOLINT(readability-identifier-naming) + template + constexpr std::invoke_result_t + operator()(ArgsT &&...Args) noexcept( + std::is_nothrow_invocable_v) { + return std::invoke(F, PSR_FWD(Args)...); + } +}; + +/// \brief A helper that transforms a statically known function pointer into a +/// callable object. +/// +/// Useful for passing functions as callbacks, while avoiding either the +/// indirect call overhead or wrapping a lambda around +template +static constexpr fn_t fn{}; // NOLINT(readability-identifier-naming) + +} // namespace psr + +#endif // PHASAR_UTILS_FN_H diff --git a/include/phasar/Utils/PointerUtils.h b/include/phasar/Utils/PointerUtils.h index 117a5bea90..2023d9d693 100644 --- a/include/phasar/Utils/PointerUtils.h +++ b/include/phasar/Utils/PointerUtils.h @@ -4,25 +4,52 @@ #include "llvm/ADT/IntrusiveRefCntPtr.h" #include +#include namespace psr { /// A simple helper function to get a raw pointer from an arbitrary pointer type /// in generic code. This overload set is extendable. +template +constexpr std::enable_if_t, T *> +getPointerFrom(T &Ref) noexcept { + return std::addressof(Ref); +} +template +constexpr std::enable_if_t, const T *> +getPointerFrom(const T &Ref) noexcept { + return std::addressof(Ref); +} +template +constexpr std::enable_if_t, T *> +getPointerFrom(T &&Ref) noexcept = delete; + template T *getPointerFrom(T *Ptr) noexcept { return Ptr; } template constexpr T *getPointerFrom(const std::unique_ptr &Ptr) noexcept { return Ptr.get(); } template +constexpr T *getPointerFrom(std::unique_ptr &Ptr) noexcept { + return Ptr.get(); +} +template constexpr T *getPointerFrom(const std::shared_ptr &Ptr) noexcept { return Ptr.get(); } template +constexpr T *getPointerFrom(std::shared_ptr &Ptr) noexcept { + return Ptr.get(); +} +template constexpr T *getPointerFrom(const llvm::IntrusiveRefCntPtr &Ptr) noexcept { return Ptr.get(); } +template +constexpr T *getPointerFrom(llvm::IntrusiveRefCntPtr &Ptr) noexcept { + return Ptr.get(); +} } // namespace psr diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 5cb0b04e8c..ffe1291f3b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -42,6 +42,9 @@ endif() if(SQLite3_FOUND) list(APPEND PHASAR_LINK_LIBS phasar_db) endif() +if(PHASAR_USE_SVF) + list(APPEND PHASAR_LINK_LIBS phasar_llvm_pointer_svf) +endif() add_phasar_library(phasar ${PHASAR_DYNLIB_KIND} FILES diff --git a/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp new file mode 100644 index 0000000000..d4833b8c18 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/AliasAnalysisView.cpp @@ -0,0 +1,34 @@ +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" + +#include "phasar/Config/phasar-config.h" + +#ifdef PHASAR_USE_SVF +#include "SVF/SVFBasedAliasAnalysis.h" +#endif + +#include + +using namespace psr; + +std::unique_ptr +AliasAnalysisView::create(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, + AliasAnalysisType PATy) { + switch (PATy) { + case AliasAnalysisType::SVFDDA: +#ifndef PHASAR_USE_SVF + throw std::runtime_error("AliasAnalysisType::SVFVFS requires SVF, which is " + "not included in your PhASAR build!"); +#else + return createSVFDDAAnalysis(IRDB); +#endif + case AliasAnalysisType::SVFVFS: +#ifndef PHASAR_USE_SVF + throw std::runtime_error("AliasAnalysisType::SVFDDA requires SVF, which is " + "not included in your PhASAR build!"); +#else + return createSVFVFSAnalysis(IRDB); +#endif + default: + return createLLVMBasedAnalysis(IRDB, UseLazyEvaluation, PATy); + } +} diff --git a/lib/PhasarLLVM/Pointer/CMakeLists.txt b/lib/PhasarLLVM/Pointer/CMakeLists.txt index 35cabf8afb..522fd55e5e 100644 --- a/lib/PhasarLLVM/Pointer/CMakeLists.txt +++ b/lib/PhasarLLVM/Pointer/CMakeLists.txt @@ -1,4 +1,4 @@ -file(GLOB_RECURSE POINTER_SRC *.h *.cpp) +file(GLOB POINTER_SRC *.cpp) add_phasar_library(phasar_llvm_pointer ${POINTER_SRC} @@ -19,3 +19,10 @@ add_phasar_library(phasar_llvm_pointer LINK_PRIVATE ${Boost_LIBRARIES} ) + +add_subdirectory(external) + +if(PHASAR_USE_SVF) + add_subdirectory(SVF) + target_link_libraries(phasar_llvm_pointer PRIVATE phasar_llvm_pointer_svf) +endif() diff --git a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp index 1eac91310a..2766f8e5a9 100644 --- a/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/FilteredLLVMAliasSet.cpp @@ -9,6 +9,8 @@ #include "phasar/Utils/NlohmannLogging.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/IR/GlobalAlias.h" +#include "llvm/IR/GlobalVariable.h" #include "llvm/IR/Instructions.h" #include "nlohmann/json_fwd.hpp" diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index 6beca389fb..7b216842c2 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -10,10 +10,12 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/BoxedPointer.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/NlohmannLogging.h" @@ -45,10 +47,7 @@ #include #include #include -#include -#include #include -#include #include namespace psr { @@ -59,7 +58,7 @@ template class AliasSetOwner; LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) - : PTA(*IRDB, UseLazyEvaluation, PATy) { + : PTA(AliasAnalysisView::create(*IRDB, UseLazyEvaluation, PATy)) { assert(IRDB != nullptr); auto NumGlobals = IRDB->getNumGlobals(); @@ -99,7 +98,7 @@ LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation, LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, const nlohmann::json &SerializedPTS) - : PTA(*IRDB, true) { + : PTA(AliasAnalysisView::create(*IRDB, true, AliasAnalysisType::Basic)) { assert(IRDB != nullptr); // Assume, we already have validated the json schema @@ -317,51 +316,13 @@ bool LLVMAliasSet::intraIsReachableAllocationSiteTy( return false; } -static llvm::Type *getPointeeTypeOrNull(const llvm::Value *Ptr) { - assert(Ptr->getType()->isPointerTy()); - - if (!Ptr->getType()->isOpaquePointerTy()) { - return Ptr->getType()->getNonOpaquePointerElementType(); - } - - if (const auto *Arg = llvm::dyn_cast(Ptr)) { - if (auto *Ty = Arg->getParamByValType()) { - return Ty; - } - if (auto *Ty = Arg->getParamStructRetType()) { - return Ty; - } - } - if (const auto *Alloca = llvm::dyn_cast(Ptr)) { - return Alloca->getAllocatedType(); - } - return nullptr; -} - -static bool mayAlias(llvm::AAResults &AA, const llvm::DataLayout &DL, +static bool mayAlias(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, const llvm::Value *Rep) { - assert(V->getType()->isPointerTy()); - assert(Rep->getType()->isPointerTy()); - - auto *ElTy = getPointeeTypeOrNull(V); - auto *RepElTy = getPointeeTypeOrNull(Rep); - - auto VSize = ElTy && ElTy->isSized() - ? llvm::LocationSize::precise(DL.getTypeStoreSize(ElTy)) - : llvm::LocationSize::precise(1); - - auto RepSize = RepElTy && RepElTy->isSized() - ? llvm::LocationSize::precise(DL.getTypeStoreSize(RepElTy)) - : llvm::LocationSize::precise(1); - if (AA.alias(V, VSize, Rep, RepSize) != llvm::AliasResult::NoAlias) { - return true; - } - - return false; + return AA.alias(V, Rep, DL) != AliasResult::NoAlias; } -void LLVMAliasSet::addPointer(llvm::AAResults &AA, const llvm::DataLayout &DL, +void LLVMAliasSet::addPointer(FunctionAliasView AA, const llvm::DataLayout &DL, const llvm::Value *V, std::vector &Reps) { llvm::SmallVector ToMerge; @@ -474,13 +435,13 @@ void LLVMAliasSet::computeFunctionsAliasSet(llvm::Function *F) { PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMAliasSet", "Analyzing function: " << F->getName()); - llvm::AAResults &AA = *PTA.getAAResults(F); + auto AA = PTA->getAAResults(F); bool EvalAAMD = true; const llvm::DataLayout &DL = F->getParent()->getDataLayout(); - auto addPointer = [this, &AA, &DL](const llvm::Value *V, // NOLINT - std::vector &Reps) { + auto addPointer = [this, AA, &DL](const llvm::Value *V, // NOLINT + std::vector &Reps) { return this->addPointer(AA, DL, V, Reps); }; @@ -567,7 +528,7 @@ void LLVMAliasSet::computeFunctionsAliasSet(llvm::Function *F) { } // we no longer need the LLVM representation - PTA.erase(F); + PTA->erase(F); } AliasResult LLVMAliasSet::alias(const llvm::Value *V1, const llvm::Value *V2, diff --git a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp index 4473b19fe9..38a79105b0 100644 --- a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.cpp @@ -7,11 +7,13 @@ * Philipp Schubert and others *****************************************************************************/ -#include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" +#include "LLVMBasedAliasAnalysis.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallVector.h" @@ -38,41 +40,34 @@ using namespace psr; namespace psr { -struct LLVMBasedAliasAnalysis::Impl { - llvm::PassBuilder PB{}; - llvm::FunctionAnalysisManager FAM{}; - llvm::FunctionPassManager FPM{}; -}; - bool LLVMBasedAliasAnalysis::hasAliasInfo(const llvm::Function &Fun) const { return AAInfos.find(&Fun) != AAInfos.end(); } void LLVMBasedAliasAnalysis::computeAliasInfo(llvm::Function &Fun) { - assert(PImpl != nullptr); - llvm::PreservedAnalyses PA = PImpl->FPM.run(Fun, PImpl->FAM); - llvm::AAResults &AAR = PImpl->FAM.getResult(Fun); + llvm::PreservedAnalyses PA = FPM.run(Fun, FAM); + llvm::AAResults &AAR = FAM.getResult(Fun); AAInfos.insert(std::make_pair(&Fun, &AAR)); } -void LLVMBasedAliasAnalysis::erase(llvm::Function *F) noexcept { +void LLVMBasedAliasAnalysis::doErase(llvm::Function *F) noexcept { // after we clear all stuff, we need to set it up for the next function-wise // analysis AAInfos.erase(F); - PImpl->FAM.clear(*F, F->getName()); + FAM.clear(*F, F->getName()); } -void LLVMBasedAliasAnalysis::clear() noexcept { +void LLVMBasedAliasAnalysis::doClear() noexcept { AAInfos.clear(); - PImpl->FAM.clear(); + FAM.clear(); } LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) - : PImpl(new Impl{}), PATy(PATy) { + : AliasAnalysisView(PATy) { - PImpl->FAM.registerPass([&] { + FAM.registerPass([&] { llvm::AAManager AA; switch (PATy) { case AliasAnalysisType::CFLAnders: @@ -95,7 +90,7 @@ LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, AA.registerFunctionAnalysis(); return AA; }); - PImpl->PB.registerFunctionAnalyses(PImpl->FAM); + PB.registerFunctionAnalyses(FAM); if (!UseLazyEvaluation) { for (auto &F : *IRDB.getModule()) { @@ -108,4 +103,66 @@ LLVMBasedAliasAnalysis::LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, LLVMBasedAliasAnalysis::~LLVMBasedAliasAnalysis() = default; +static AliasResult translateAAResult(llvm::AliasResult Res) noexcept { + switch (Res) { + case llvm::AliasResult::NoAlias: + return AliasResult::NoAlias; + case llvm::AliasResult::MayAlias: + return AliasResult::MayAlias; + case llvm::AliasResult::PartialAlias: + return AliasResult::PartialAlias; + case llvm::AliasResult::MustAlias: + return AliasResult::MustAlias; + } +} + +static llvm::Type *getPointeeTypeOrNull(const llvm::Value *Ptr) { + assert(Ptr->getType()->isPointerTy()); + + if (!Ptr->getType()->isOpaquePointerTy()) { + return Ptr->getType()->getNonOpaquePointerElementType(); + } + + if (const auto *Arg = llvm::dyn_cast(Ptr)) { + if (auto *Ty = Arg->getParamByValType()) { + return Ty; + } + if (auto *Ty = Arg->getParamStructRetType()) { + return Ty; + } + } + if (const auto *Alloca = llvm::dyn_cast(Ptr)) { + return Alloca->getAllocatedType(); + } + return nullptr; +} + +AliasResult LLVMBasedAliasAnalysis::aliasImpl(llvm::AAResults *AA, + const llvm::Value *V, + const llvm::Value *Rep, + const llvm::DataLayout &DL) { + + assert(V->getType()->isPointerTy()); + assert(Rep->getType()->isPointerTy()); + + auto *ElTy = getPointeeTypeOrNull(V); + auto *RepElTy = getPointeeTypeOrNull(Rep); + + auto VSize = ElTy && ElTy->isSized() + ? llvm::LocationSize::precise(DL.getTypeStoreSize(ElTy)) + : llvm::LocationSize::precise(1); + + auto RepSize = RepElTy && RepElTy->isSized() + ? llvm::LocationSize::precise(DL.getTypeStoreSize(RepElTy)) + : llvm::LocationSize::precise(1); + + return translateAAResult(AA->alias(V, VSize, Rep, RepSize)); +} + +std::unique_ptr AliasAnalysisView::createLLVMBasedAnalysis( + LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy) { + return std::make_unique(IRDB, UseLazyEvaluation, + PATy); +} + } // namespace psr diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h similarity index 55% rename from include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h rename to lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h index 1195da6c82..2195d02ff4 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h @@ -10,9 +10,13 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMBASEDALIASANALYSIS_H_ #define PHASAR_PHASARLLVM_POINTER_LLVMBASEDALIASANALYSIS_H_ +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" #include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Utils/Fn.h" #include "llvm/Analysis/AliasAnalysis.h" +#include "llvm/Passes/PassBuilder.h" namespace llvm { class Value; @@ -25,47 +29,42 @@ namespace psr { class LLVMProjectIRDB; -class LLVMBasedAliasAnalysis { - +class LLVMBasedAliasAnalysis : public AliasAnalysisView { public: explicit LLVMBasedAliasAnalysis( LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, AliasAnalysisType PATy = AliasAnalysisType::Basic); - LLVMBasedAliasAnalysis(LLVMBasedAliasAnalysis &&) noexcept = default; - LLVMBasedAliasAnalysis & - operator=(LLVMBasedAliasAnalysis &&) noexcept = default; - - LLVMBasedAliasAnalysis(const LLVMBasedAliasAnalysis &) = delete; - LLVMBasedAliasAnalysis &operator=(const LLVMBasedAliasAnalysis &) = delete; - ~LLVMBasedAliasAnalysis(); + ~LLVMBasedAliasAnalysis() override; - [[nodiscard]] inline llvm::AAResults *getAAResults(llvm::Function *F) { +private: + FunctionAliasView doGetAAResults(const llvm::Function *F) override { if (!hasAliasInfo(*F)) { - computeAliasInfo(*F); + // NOLINTNEXTLINE - FIXME when it is fixed in LLVM + computeAliasInfo(const_cast(*F)); } - return AAInfos.lookup(F); + return createFAView(AAInfos.lookup(F)); }; - void erase(llvm::Function *F) noexcept; + void doErase(llvm::Function *F) noexcept override; - void clear() noexcept; + void doClear() noexcept override; - [[nodiscard]] inline AliasAnalysisType - getPointerAnalysisType() const noexcept { - return PATy; - }; + static AliasResult aliasImpl(llvm::AAResults *, const llvm::Value *, + const llvm::Value *, const llvm::DataLayout &); + [[nodiscard]] constexpr FunctionAliasView + createFAView(llvm::AAResults *AAR) noexcept { + return {AAR, fn}; + } -private: [[nodiscard]] bool hasAliasInfo(const llvm::Function &Fun) const; void computeAliasInfo(llvm::Function &Fun); // -- data members - - struct Impl; - std::unique_ptr PImpl; - AliasAnalysisType PATy; + llvm::PassBuilder PB; + llvm::FunctionAnalysisManager FAM; + llvm::FunctionPassManager FPM; llvm::DenseMap AAInfos; }; diff --git a/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt b/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt new file mode 100644 index 0000000000..ec59ba7f2b --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/CMakeLists.txt @@ -0,0 +1,24 @@ +file(GLOB_RECURSE SVF_AA_SRC *.h *.cpp) + +add_phasar_library(phasar_llvm_pointer_svf + ${SVF_AA_SRC} + + LINKS + phasar_utils + phasar_pointer + phasar_llvm_utils + phasar_llvm_db + + LLVM_LINK_COMPONENTS + Core + Support + Analysis + Passes + Demangle + + LINK_PRIVATE + SvfLLVM SvfCore ${Z3_LIBRARIES} +) + +target_include_directories(phasar_llvm_pointer_svf SYSTEM PRIVATE ${SVF_INSTALL_INCLUDE_DIR}) +target_link_directories(phasar_llvm_pointer_svf PUBLIC ${SVF_INSTALL_LIB_DIR}) diff --git a/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp b/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp new file mode 100644 index 0000000000..32376f4ae2 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/InitSVF.cpp @@ -0,0 +1,46 @@ +#include "InitSVF.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" + +#include "SVF-LLVM/LLVMModule.h" +#include "Util/CommandLine.h" +#include "Util/DPItem.h" +#include "Util/Options.h" + +static psr::EmptyType initializeSVFImpl() { + char EmptyStr[] = ""; + char NoAliasCheck[] = "-alias-check=false"; + char NoStat[] = "-stat=false"; + char *MockArgv[] = { + EmptyStr, + NoAliasCheck, + NoStat, + }; + OptionBase::parseOptions(std::size(MockArgv), MockArgv, "", ""); + + // Initialize these parameters to their default values. + // See https://github.com/SVF-tools/SVF/blob/master/svf/lib/DDA/DDAPass.cpp + // for reference + SVF::ContextCond::setMaxCxtLen(SVF::Options::MaxContextLen()); + SVF::ContextCond::setMaxPathLen(SVF::Options::MaxPathLen()); + + return psr::EmptyType{}; +} + +void psr::initializeSVF() { + static const auto SVFInitialized = initializeSVFImpl(); + (void)SVFInitialized; +} + +SVF::SVFModule *psr::initSVFModule(psr::LLVMProjectIRDB &IRDB) { + psr::initializeSVF(); + + auto *Mod = SVF::LLVMModuleSet::buildSVFModule(*IRDB.getModule()); + if (!Mod) { + throw std::runtime_error( + "SVF failed to create an SVFModule from an llvm::Module!"); + } + + return Mod; +} diff --git a/lib/PhasarLLVM/Pointer/SVF/InitSVF.h b/lib/PhasarLLVM/Pointer/SVF/InitSVF.h new file mode 100644 index 0000000000..2599a3626e --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/InitSVF.h @@ -0,0 +1,15 @@ +#pragma once + +#include "llvm/Support/Compiler.h" + +namespace SVF { +class SVFModule; +} // namespace SVF + +namespace psr { +class LLVMProjectIRDB; + +LLVM_LIBRARY_VISIBILITY void initializeSVF(); +LLVM_LIBRARY_VISIBILITY SVF::SVFModule * +initSVFModule(psr::LLVMProjectIRDB &IRDB); +} // namespace psr diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp new file mode 100644 index 0000000000..0416a8415b --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.cpp @@ -0,0 +1,123 @@ +#include "SVFBasedAliasAnalysis.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Utils/Fn.h" + +#include "DDA/ContextDDA.h" +#include "DDA/DDAClient.h" +#include "InitSVF.h" +#include "SVF-LLVM/SVFIRBuilder.h" +#include "SVFIR/SVFIR.h" +#include "SVFIR/SVFModule.h" +#include "SVFIR/SVFType.h" +#include "WPA/Andersen.h" +#include "WPA/VersionedFlowSensitive.h" + +#include +#include + +namespace psr { +static constexpr psr::AliasResult +translateSVFAliasResult(SVF::AliasResult AR) noexcept { + switch (AR) { + case SVF::NoAlias: + return AliasResult::NoAlias; + case SVF::MayAlias: + return AliasResult::MayAlias; + case SVF::MustAlias: + return AliasResult::MustAlias; + case SVF::PartialAlias: + return AliasResult::PartialAlias; + } +} + +static psr::AliasResult aliasImpl(SVF::PointerAnalysis *AA, + const llvm::Value *V, const llvm::Value *Rep, + const llvm::DataLayout & /*DL*/) { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod1 = ModSet->getSVFValue(V); + auto *Nod2 = ModSet->getSVFValue(Rep); + + if (!Nod1 || !Nod2) { + return AliasResult::MayAlias; + } + + return translateSVFAliasResult(AA->alias(Nod1, Nod2)); +} + +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class SVFAliasAnalysisBase : public AliasAnalysisView { +public: + SVFAliasAnalysisBase(SVF::SVFModule *Mod, AliasAnalysisType PATy) + : AliasAnalysisView(PATy), IRBuilder(Mod), PAG(IRBuilder.build()) {} + + ~SVFAliasAnalysisBase() override { + SVF::SVFIR::releaseSVFIR(); + SVF::AndersenWaveDiff::releaseAndersenWaveDiff(); + SVF::SymbolTableInfo::releaseSymbolInfo(); + SVF::LLVMModuleSet::releaseLLVMModuleSet(); + } + +private: + void doErase(llvm::Function *F) noexcept override {} + void doClear() noexcept override {} + +protected: + SVF::SVFIRBuilder IRBuilder; + SVF::SVFIR *PAG; +}; + +class SVFVFSAnalysis : public SVFAliasAnalysisBase { +public: + SVFVFSAnalysis(SVF::SVFModule *Mod) + : SVFAliasAnalysisBase(Mod, AliasAnalysisType::SVFVFS), + // Note: We must use the static createVFSWPA() function, otherwise SVF + // will leak memory + VFS(SVF::VersionedFlowSensitive::createVFSWPA(PAG)) {} + + ~SVFVFSAnalysis() override { SVF::VersionedFlowSensitive::releaseVFSWPA(); } + +private: + FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { + return {VFS, fn}; + } + + SVF::VersionedFlowSensitive *VFS; +}; + +class SVFDDAAnalysis : public SVFAliasAnalysisBase { +public: + SVFDDAAnalysis(SVF::SVFModule *Mod) + : SVFAliasAnalysisBase(Mod, AliasAnalysisType::SVFVFS), Client(Mod) { + Client.initialise(Mod); + DDA.emplace(PAG, &Client); + DDA->initialize(); + Client.answerQueries(&*DDA); + DDA->finalize(); + } + +private: + FunctionAliasView doGetAAResults(const llvm::Function * /*F*/) override { + return {&*DDA, fn}; + } + + SVF::DDAClient Client; + std::optional DDA; +}; + +} // namespace psr + +[[nodiscard]] auto psr::createSVFVFSAnalysis(LLVMProjectIRDB &IRDB) + -> std::unique_ptr { + + return std::make_unique(psr::initSVFModule(IRDB)); +} + +[[nodiscard]] auto psr::createSVFDDAAnalysis(LLVMProjectIRDB &IRDB) + -> std::unique_ptr { + + return std::make_unique(psr::initSVFModule(IRDB)); +} diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h new file mode 100644 index 0000000000..ec5955a0df --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFBasedAliasAnalysis.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_POINTER_SVFBASEDALIASANALYSIS_H +#define PHASAR_PHASARLLVM_POINTER_SVFBASEDALIASANALYSIS_H + +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" + +#include + +namespace psr { +[[nodiscard]] std::unique_ptr +createSVFVFSAnalysis(LLVMProjectIRDB &IRDB); + +[[nodiscard]] std::unique_ptr +createSVFDDAAnalysis(LLVMProjectIRDB &IRDB); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_SVFBASEDALIASANALYSIS_H diff --git a/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp new file mode 100644 index 0000000000..1aa5744403 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/SVF/SVFPointsToSet.cpp @@ -0,0 +1,153 @@ +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" + +#include "phasar/Pointer/PointsToInfoBase.h" + +#include "DDA/ContextDDA.h" +#include "DDA/DDAClient.h" +#include "InitSVF.h" +#include "MemoryModel/PointerAnalysis.h" +#include "SVF-LLVM/LLVMModule.h" +#include "SVF-LLVM/SVFIRBuilder.h" +#include "WPA/Andersen.h" + +#include + +namespace { +template class SVFPointsToSet; +struct DDAPointsToSetImpl; +struct VFSPointsToSetImpl; +} // namespace + +namespace psr { +template +struct PointsToTraits> : SVFPointsToInfoTraits {}; +template <> +struct PointsToTraits : SVFPointsToInfoTraits {}; +template <> +struct PointsToTraits : SVFPointsToInfoTraits {}; +} // namespace psr + +namespace { + +template +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +class SVFPointsToSet : public psr::PointsToInfoBase> { + using base_t = psr::PointsToInfoBase>; + friend base_t; + +public: + using typename base_t::n_t; + using typename base_t::o_t; + using typename base_t::PointsToSetPtrTy; + using typename base_t::PointsToSetTy; + using typename base_t::v_t; + + ~SVFPointsToSet() { + SVF::SVFIR::releaseSVFIR(); + SVF::AndersenWaveDiff::releaseAndersenWaveDiff(); + SVF::SymbolTableInfo::releaseSymbolInfo(); + SVF::LLVMModuleSet::releaseLLVMModuleSet(); + } + +private: + SVFPointsToSet(SVF::SVFModule *Mod) + : IRBuilder(Mod), PAG(IRBuilder.build()) {} + + [[nodiscard]] constexpr Derived &self() noexcept { + return static_cast(*this); + } + [[nodiscard]] constexpr const Derived &self() const noexcept { + return static_cast(*this); + } + + [[nodiscard]] o_t + asAbstractObjectImpl(psr::ByConstRef Pointer) const noexcept { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + auto *Nod = ModSet->getSVFValue(Pointer); + + return PAG->getValueNode(Nod); + } + + [[nodiscard]] std::optional asPointerOrNullImpl(o_t Obj) const noexcept { + if (const auto *Val = PAG->getObject(Obj)->getValue()) { + auto *ModSet = SVF::LLVMModuleSet::getLLVMModuleSet(); + if (const auto *LLVMVal = ModSet->getLLVMValue(Val)) { + return LLVMVal; + } + } + + return std::nullopt; + } + + bool mayPointsToImpl(o_t Pointer, o_t Obj, n_t /*AtInstruction*/) const { + auto &PTA = self().getPTA(); + const auto &Pts = PTA.getPts(Pointer); + return Pts.test(Obj); + } + + using base_t::mayPointsToImpl; + + PointsToSetPtrTy getPointsToSetImpl(o_t Pointer, + n_t /*AtInstruction*/) const { + auto &PTA = self().getPTA(); + const auto &Pts = PTA.getPts(Pointer); + + // TODO: Should we cache this? + PointsToSetPtrTy Ret; + Ret.reserve(Pts.count()); + Ret.insert(Pts.begin(), Pts.end()); + return Ret; + } + + using base_t::getPointsToSetImpl; + +protected: + SVF::SVFIRBuilder IRBuilder; + SVF::SVFIR *PAG; + friend Derived; +}; + +struct VFSPointsToSetImpl : SVFPointsToSet { + VFSPointsToSetImpl(SVF::SVFModule *Mod) + : SVFPointsToSet(Mod), + // Note: We must use the static createVFSWPA() function, otherwise SVF + // will leak memory + VFS(SVF::VersionedFlowSensitive::createVFSWPA(PAG)) {} + + ~VFSPointsToSetImpl() { SVF::VersionedFlowSensitive::releaseVFSWPA(); } + + [[nodiscard]] SVF::PointerAnalysis &getPTA() const noexcept { return *VFS; } + + SVF::VersionedFlowSensitive *VFS; +}; + +struct DDAPointsToSetImpl : SVFPointsToSet { + DDAPointsToSetImpl(SVF::SVFModule *Mod) : SVFPointsToSet(Mod), Client(Mod) { + Client.initialise(Mod); + DDA.emplace(PAG, &Client); + DDA->initialize(); + Client.answerQueries(&*DDA); + DDA->finalize(); + } + + [[nodiscard]] SVF::PointerAnalysis &getPTA() const noexcept { return *DDA; } + + SVF::DDAClient Client; + + // Note: SVF is not thread-safe anyway, so this 'mutable' should not be a + // problem + mutable std::optional DDA; +}; +} // namespace + +auto psr::createSVFVFSPointsToInfo(LLVMProjectIRDB &IRDB) + -> SVFBasedPointsToInfo { + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); +} + +auto psr::createSVFDDAPointsToInfo(LLVMProjectIRDB &IRDB) + -> SVFBasedPointsToInfo { + return SVFBasedPointsToInfo(std::in_place_type, + psr::initSVFModule(IRDB)); +} diff --git a/lib/PhasarLLVM/Pointer/external/CMakeLists.txt b/lib/PhasarLLVM/Pointer/external/CMakeLists.txt new file mode 100644 index 0000000000..185b5103ea --- /dev/null +++ b/lib/PhasarLLVM/Pointer/external/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB_RECURSE LLVM_AA_SRC *.h *.cpp) +target_sources(phasar_llvm_pointer PRIVATE ${LLVM_AA_SRC}) diff --git a/lib/Pointer/PointsToInfo.cpp b/lib/Pointer/PointsToInfo.cpp index df3079fcea..ed3ca3b485 100644 --- a/lib/Pointer/PointsToInfo.cpp +++ b/lib/Pointer/PointsToInfo.cpp @@ -11,6 +11,7 @@ #include "phasar/Pointer/PointsToInfoBase.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/PointerUtils.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseSet.h" @@ -60,11 +61,6 @@ class DummyFieldInsensitivePointsToAnalysis static PointsToSetTy Empty{}; return &Empty; } - - [[nodiscard]] std::vector - getInterestingPointersAtImpl(ByConstRef /*AtInstruction*/) const { - return {}; - } }; template class PointsToInfoBase; @@ -129,23 +125,16 @@ class DummyFieldSensitivePointsToAnalysis static PointsToSetTy Empty{}; return &Empty; } - - [[nodiscard]] std::vector - getInterestingPointersAtImpl(ByConstRef /*AtInstruction*/) const { - return {}; - } }; [[maybe_unused]] void testTypeErasure() { DummyFieldInsensitivePointsToAnalysis PTA1; [[maybe_unused]] PointsToInfoRef< - PointsToTraits> - TEPTA1 = &PTA1; + PointsToTraits> TEPTA1 = &PTA1; DummyFieldSensitivePointsToAnalysis PTA2; [[maybe_unused]] PointsToInfoRef< - PointsToTraits> - TEPTA2 = &PTA2; + PointsToTraits> TEPTA2 = &PTA2; PointsToInfo> TEPTA3( std::in_place_type); @@ -154,7 +143,7 @@ class DummyFieldSensitivePointsToAnalysis std::in_place_type); // Make sure, the template gets instantiated: - std::ignore = TEPTA1.getInterestingPointersAt(nullptr); + std::ignore = TEPTA1.getPointsToSet({}, nullptr); } template class PointsToInfoBase; diff --git a/unittests/PhasarLLVM/Pointer/CMakeLists.txt b/unittests/PhasarLLVM/Pointer/CMakeLists.txt index 8cf7152adf..290062d69e 100644 --- a/unittests/PhasarLLVM/Pointer/CMakeLists.txt +++ b/unittests/PhasarLLVM/Pointer/CMakeLists.txt @@ -1,9 +1,17 @@ -set(ControlFlowSources +set(PointerFlowSources LLVMAliasSetTest.cpp LLVMAliasSetSerializationTest.cpp FilteredLLVMAliasSetTest.cpp ) -foreach(TEST_SRC ${ControlFlowSources}) +if (PHASAR_USE_SVF) + list(APPEND PointerFlowSources SVFAliasSetTest.cpp) +endif() + +foreach(TEST_SRC ${PointerFlowSources}) add_phasar_unittest(${TEST_SRC}) endforeach(TEST_SRC) + +if(PHASAR_USE_SVF) + target_link_libraries(SVFAliasSetTest PRIVATE phasar_llvm_pointer_svf) +endif() diff --git a/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp new file mode 100644 index 0000000000..4482d6071e --- /dev/null +++ b/unittests/PhasarLLVM/Pointer/SVFAliasSetTest.cpp @@ -0,0 +1,92 @@ + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasResult.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +using namespace psr; + +TEST(SVFAliasSetTest, Alias_01) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + LLVMAliasSet AS(&IRDB, false, AliasAnalysisType::SVFVFS); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alias = IRDB.getInstruction(0); + + auto ASet = AS.getAliasSet(V); + LLVMAliasSet::AliasSetTy GroundTruth = {V, Alias}; + EXPECT_EQ(GroundTruth, *ASet); + EXPECT_NE(AliasResult::NoAlias, AS.alias(V, Alias)); +} + +TEST(SVFAliasSetTest, Alias_02) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + LLVMAliasSet AS(&IRDB, false, AliasAnalysisType::SVFDDA); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alias = IRDB.getInstruction(0); + + auto ASet = AS.getAliasSet(V); + LLVMAliasSet::AliasSetTy GroundTruth = {V, Alias}; + EXPECT_EQ(GroundTruth, *ASet); + EXPECT_NE(AliasResult::NoAlias, AS.alias(V, Alias)); +} + +TEST(SVFAliasSetTest, PointsTo_01) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + auto PT = createSVFVFSPointsToInfo(IRDB); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alloc = IRDB.getInstruction(0); + + auto PSet = PT.getPointsToSet(V, V->getNextNode()); + ASSERT_EQ(1, PSet.size()); + EXPECT_EQ(Alloc, PT.asPointerOrNull(*PSet.begin())); + + auto AllocObjs = PT.getPointsToSet(Alloc, Alloc->getNextNode()); + ASSERT_EQ(1, AllocObjs.size()); + auto AllocObj = *AllocObjs.begin(); + EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); +} + +TEST(SVFAliasSetTest, PointsTo_02) { + LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + + "pointers/basic_01_cpp_dbg.ll"); + + auto PT = createSVFDDAPointsToInfo(IRDB); + + const auto *V = IRDB.getInstruction(5); + ASSERT_TRUE(V && V->getType()->isPointerTy()); + + const auto *Alloc = IRDB.getInstruction(0); + + auto PSet = PT.getPointsToSet(V, V->getNextNode()); + ASSERT_EQ(1, PSet.size()); + EXPECT_EQ(Alloc, PT.asPointerOrNull(*PSet.begin())); + + auto AllocObjs = PT.getPointsToSet(Alloc, Alloc->getNextNode()); + ASSERT_EQ(1, AllocObjs.size()); + auto AllocObj = *AllocObjs.begin(); + EXPECT_TRUE(PT.mayPointsTo(V, AllocObj, V->getNextNode())); +} + +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +} From b56205dab64e85095ac1de3f34a5888c9d85ae65 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 26 May 2025 18:05:19 +0200 Subject: [PATCH 08/24] More Heap-Allocating Functions (#772) * Add more known heap-allocating functions + add singleton-returning functions * Add two more heap allocating functions --- include/phasar/Utils/LibrarySummary.h | 27 ++++ lib/PhasarLLVM/Utils/LLVMShorthands.cpp | 13 +- lib/Utils/LibrarySummary.cpp | 160 ++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 include/phasar/Utils/LibrarySummary.h create mode 100644 lib/Utils/LibrarySummary.cpp diff --git a/include/phasar/Utils/LibrarySummary.h b/include/phasar/Utils/LibrarySummary.h new file mode 100644 index 0000000000..348ce0eedd --- /dev/null +++ b/include/phasar/Utils/LibrarySummary.h @@ -0,0 +1,27 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_LIBRARY_SUMMARY_H +#define PHASAR_UTILS_LIBRARY_SUMMARY_H + +#include "llvm/ADT/StringRef.h" + +namespace psr { + +/// Checks, whether a function with the given (mangled) name is known to be a +/// heap-allocating function, similar to malloc. +[[nodiscard]] bool isHeapAllocatingFunction(llvm::StringRef FName) noexcept; + +/// Checks whether a function with the given (mangled) name is known to always +/// return the same pointer +[[nodiscard]] bool isSingletonReturningFunction(llvm::StringRef FName) noexcept; + +} // namespace psr + +#endif // PHASAR_UTILS_LIBRARY_SUMMARY_H diff --git a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp index 99f8095137..2b8fac6aa3 100644 --- a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp +++ b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp @@ -17,11 +17,11 @@ #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Config/Configuration.h" +#include "phasar/Utils/LibrarySummary.h" #include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/Constants.h" @@ -67,9 +67,14 @@ bool psr::isAllocaInstOrHeapAllocaFunction(const llvm::Value *V) noexcept { } bool psr::isHeapAllocatingFunction(const llvm::Function *Fun) noexcept { - return llvm::StringSwitch(Fun->getName()) - .Cases("_Znwm", "_Znam", "_ZnwPv", "malloc", "calloc", "realloc", true) - .Default(false); + auto FunName = Fun->getName(); + + if (FunName == "realloc") { + // For backwards compatibility. We should treat realloc specially. + return true; + } + + return isHeapAllocatingFunction(FunName); } // For C-style polymorphism we need to check whether a callee candidate would diff --git a/lib/Utils/LibrarySummary.cpp b/lib/Utils/LibrarySummary.cpp new file mode 100644 index 0000000000..b6850eb4df --- /dev/null +++ b/lib/Utils/LibrarySummary.cpp @@ -0,0 +1,160 @@ +#include "phasar/Utils/LibrarySummary.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" + +static constexpr llvm::StringLiteral HeapAllocatingFunNames[] = { + // Also see SVF's extapi.c + + // C functions: + "malloc", + "calloc", + "fopen", + "fopen64", + "fdopen", + "readdir64", + "tmpvoid64", + "zmalloc", + "gzdopen", + "iconv_open", + "lalloc", + "lalloc_clear", + "nhalloc", + "oballoc", + "popen", + "pthread_getspecific", + "readdir", + "safe_calloc", + "safe_malloc", + "safecalloc", + "safemalloc", + "setmntent", + "shmat", + "__sysv_signal", + "signal", + "tempnam", + "tmpvoid", + "xcalloc", + "xmalloc", + "xnmalloc", + "xcharalloc", + "xinmalloc", + "xizalloc", + "xzalloc", + "xpalloc", + "xicalloc", + "icalloc", + "imalloc", + "_gl_alloc_nomem", + "aligned_alloc", + "memalign", + "valloc", + "mmap64", + "XSetLocaleModifiers", + "__strdup", + "xmemdup", + "crypt", + "ctime", + "dlerror", + "dlopen", + "gai_strerror", + "gcry_cipher_algo_name", + "svfgcry_md_algo_name_", + "getenv", + "getlogin", + "getpass", + "gnutls_strerror", + "gpg_strerror", + "gzerror", + "inet_ntoa", + "initscr", + "llvm_stacksave", + "mmap", + "newwin", + "nl_langinfo", + "opendir", + "sbrk", + "strdup", + "xstrdup", + "strerror", + "strsignal", + "textdomain", + "tgetstr", + "tigetstr", + "tmpnam", + "ttyname", + "tzalloc", + + // C++ functions: + "_Znwm", + "_Znam", + "_Znwj", + "_Znaj", + "_ZnwmRKSt9nothrow_t", + "_ZnamRKSt9nothrow_t", + "_ZnwmSt11align_val_t", + "_ZnamSt11align_val_t", + "_ZnwmSt11align_val_tRKSt9nothrow_t", + "_ZnamSt11align_val_tRKSt9nothrow_t", + "__cxa_allocate_exception", +}; + +static constexpr llvm::StringLiteral SingletonReturningFunctions[] = { + "__ctype_b_loc", + "__ctype_tolower_loc", + "__ctype_toupper_loc", + "__errno_location", + "__h_errno_location", + "__res_state", + "asctime", + "bindtextdomain", + "bind_textdomain_codeset", + "dcgettext", + "dgettext", + "dngettext", + "getgrgid", + "getgrnam", + "gethostbyaddr", + "gethostbyname", + "gethostbyname2", + "getmntent", + "getprotobyname", + "getprotobynumber", + "getpwent", + "getpwnam", + "getpwuid", + "getservbyname", + "getservbyport", + "getspnam", + "gettext", + "gmtime", + "gnu_get_libc_version", + "gnutls_check_version", + "localeconv", + "localtime", + "ngettext", + "pango_cairo_font_map_get_default", + "re_comp", + "setlocale", + "tgoto", + "tparm", + "zError", +}; + +bool psr::isHeapAllocatingFunction(llvm::StringRef FName) noexcept { + // Note: For a performance comparison of different search strategies, see + // https://quick-bench.com/q/lNjDT6z-M-L08h372fbCfYjwM64 + + static const llvm::DenseSet HAFs( + std::begin(HeapAllocatingFunNames), std::end(HeapAllocatingFunNames)); + + return HAFs.count(FName); +} + +bool psr::isSingletonReturningFunction(llvm::StringRef FName) noexcept { + static const llvm::DenseSet SRFs( + std::begin(SingletonReturningFunctions), + std::end(SingletonReturningFunctions)); + + return SRFs.count(FName); +} From ed7995aad6c0628eee94f32a5477be0b120ada6a Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Wed, 28 May 2025 17:50:17 +0200 Subject: [PATCH 09/24] Improved Code Documentation (#771) * comments to doxygen * some more descriptions * Overhauled README.dox * Added texts + fixed typos * More descriptions and typo fixes * Revert change in LLVM code * Fix existing docs * Some more comments + update Doxyfile.in * Add gh-pages deploy script * pre-commit * Small compiler fix * pre-commit with newer clang version * install Doxygen in CI * test * add token * Remove gh-pages deploy trigger on pull-request * install graphviz in CI to draw pretty class diagrams * update website * Remove gh-pages deploy trigger on pull-request * Apply review comments --------- Co-authored-by: mxHuber --- .github/workflows/deploy-docs.yml | 41 ++ README.md | 2 +- docs/Doxyfile.in | 46 +- docs/README.dox | 25 +- .../phasar/AnalysisStrategy/AnalysisSetup.h | 2 +- include/phasar/ControlFlow/CallGraphData.h | 6 +- include/phasar/ControlFlow/ICFGBase.h | 2 +- include/phasar/DB/ProjectIRDBBase.h | 2 +- .../phasar/DataFlow/IfdsIde/EdgeFunction.h | 26 +- .../phasar/DataFlow/IfdsIde/EdgeFunctions.h | 405 +++++++------- .../phasar/DataFlow/IfdsIde/FlowFunctions.h | 522 +++++++++--------- .../DataFlow/IfdsIde/IDETabulationProblem.h | 17 + .../DataFlow/IfdsIde/IFDSIDESolverConfig.h | 18 + .../DataFlow/IfdsIde/IFDSTabulationProblem.h | 17 + .../phasar/DataFlow/IfdsIde/InitialSeeds.h | 3 + .../DataFlow/IfdsIde/Solver/Compressor.h | 16 + .../IfdsIde/Solver/FlowFunctionCache.h | 4 + .../IfdsIde/Solver/GenericSolverResults.h | 2 +- .../DataFlow/IfdsIde/Solver/IFDSSolver.h | 8 + .../IfdsIde/Solver/IterativeIDESolver.h | 2 +- .../phasar/DataFlow/IfdsIde/SolverResults.h | 22 +- .../DataFlow/IfdsIde/SpecialSummaries.h | 26 +- .../DataFlow/Mono/Contexts/CallStringCTX.h | 4 + .../phasar/DataFlow/Mono/InterMonoProblem.h | 11 + .../phasar/DataFlow/Mono/IntraMonoProblem.h | 13 +- .../DataFlow/Mono/Solver/InterMonoSolver.h | 6 + .../DataFlow/Mono/Solver/IntraMonoSolver.h | 4 + .../PathSensitivity/PathSensitivityManager.h | 2 + .../PathSensitivityManagerMixin.h | 20 + include/phasar/Domain/AnalysisDomain.h | 39 +- .../ControlFlow/GlobalCtorsDtorsModel.h | 21 +- .../ControlFlow/LLVMBasedBackwardCFG.h | 2 + .../ControlFlow/LLVMBasedBackwardICFG.h | 4 +- .../PhasarLLVM/ControlFlow/LLVMBasedCFG.h | 2 + .../PhasarLLVM/ControlFlow/LLVMBasedICFG.h | 2 + .../ControlFlow/LLVMVFTableProvider.h | 6 + .../ControlFlow/Resolver/CHAResolver.h | 3 + .../ControlFlow/Resolver/NOResolver.h | 1 + .../ControlFlow/Resolver/OTFResolver.h | 2 + .../ControlFlow/Resolver/RTAResolver.h | 3 + .../ControlFlow/Resolver/Resolver.h | 4 + .../ControlFlow/SparseLLVMBasedCFG.h | 2 + .../ControlFlow/SparseLLVMBasedICFG.h | 8 + .../ControlFlow/SparseLLVMBasedICFGView.h | 11 +- .../phasar/PhasarLLVM/DB/LLVMProjectIRDB.h | 1 + .../DataFlow/IfdsIde/FunctionDataFlowFacts.h | 7 +- .../IfdsIde/LLVMFunctionDataFlowFacts.h | 9 +- .../DataFlow/IfdsIde/LLVMZeroValue.h | 7 +- .../Problems/IDEExtendedTaintAnalysis.h | 2 + .../Z3BasedPathSensitvityManager.h | 14 + .../PhasarLLVM/Domain/LLVMAnalysisDomain.h | 4 + .../phasar/PhasarLLVM/Pointer/LLVMAliasSet.h | 33 +- .../PhasarLLVM/Pointer/LLVMAliasSetData.h | 3 + .../TypeHierarchy/DIBasedTypeHierarchy.h | 26 +- .../TypeHierarchy/DIBasedTypeHierarchyData.h | 3 + include/phasar/PhasarLLVM/Utils/Annotation.h | 5 +- include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h | 7 + .../phasar/PhasarLLVM/Utils/LLVMShorthands.h | 20 +- include/phasar/PhasarPass/PhasarPass.h | 14 + include/phasar/Pointer/AliasInfo.h | 15 +- include/phasar/TypeHierarchy/VFTable.h | 1 + include/phasar/Utils/AnalysisPrinterBase.h | 2 + include/phasar/Utils/DebugOutput.h | 3 + include/phasar/Utils/DefaultAnalysisPrinter.h | 5 +- include/phasar/Utils/EquivalenceClassMap.h | 43 +- include/phasar/Utils/ErrorHandling.h | 4 + include/phasar/Utils/IO.h | 3 + .../phasar/Utils/OnTheFlyAnalysisPrinter.h | 9 +- include/phasar/Utils/Utilities.h | 12 +- .../Pointer/LLVMBasedAliasAnalysis.h | 4 + .../TaintConfig/LLVMTaintConfig.cpp | 2 +- 71 files changed, 1014 insertions(+), 628 deletions(-) create mode 100644 .github/workflows/deploy-docs.yml diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml new file mode 100644 index 0000000000..7588c25fd2 --- /dev/null +++ b/.github/workflows/deploy-docs.yml @@ -0,0 +1,41 @@ +name: Build and Deploy Doxygen Docs +on: + push: + branches: [ development ] + # pull_request: # For testing only. Remove before merge! + # branches: [ development ] +permissions: + contents: write +jobs: + build-and-deploy: + runs-on: ubuntu-24.04 + strategy: + fail-fast: true + continue-on-error: false + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + + - name: Install Phasar Dependencies + shell: bash + run: | + ./utils/InstallAptDependencies.sh --noninteractive tzdata doxygen graphviz + + - name: Build Doxygen Docs + shell: bash + env: + CXX: clang++-15 + CC: clang-15 + run: | + cmake -S . -B build -DPHASAR_BUILD_DOC=ON + cmake --build ./build --target doc_doxygen + + - name: Deploy Doxygen Docs on GitHub Pages + uses: JamesIves/github-pages-deploy-action@v4 + with: + folder: build/docs/html + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/README.md b/README.md index d15020fae3..17396e5464 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Currently, PhASAR is maintained by PhASAR requires at least C++-17. However, building in C++20 mode is supported. You may enable this setting the cmake variable `CMAKE_CXX_STANDARD` to `20`. -Although phasar currently does not make use of C++-20 features (except for some `concept`s behind an #ifdef border), your client application that just *uses* phasar as a library may want to use C++20 ealier. +Although phasar currently does not make use of C++-20 features (except for some `concept`s behind an #ifdef border), your client application that just *uses* phasar as a library may want to use C++20 earlier. ## Currently Supported Version of LLVM diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index b45f14eebd..2014d73648 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "PhASAR" +PROJECT_NAME = PhASAR # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = @PHASAR_VERSION@ +PROJECT_NUMBER = @PHASAR_VERSION@@development # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "PhASAR a LLVM-based Static Analysis Framework" +PROJECT_BRIEF = "A LLVM-based Static Analysis Framework" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels @@ -144,7 +144,7 @@ FULL_PATH_NAMES = YES # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. -STRIP_FROM_PATH = +STRIP_FROM_PATH = @PHASAR_SRC_DIR@ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which @@ -153,8 +153,7 @@ STRIP_FROM_PATH = # specify the list of include paths that are normally passed to the compiler # using the -I flag. -STRIP_FROM_INC_PATH = - +STRIP_FROM_INC_PATH = @PHASAR_SRC_DIR@/include # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. @@ -398,7 +397,7 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = NO +EXTRACT_ALL = YES # If the EXTRACT_PRIVATE tag is set to YES all private members of a class will # be included in the documentation. @@ -743,7 +742,8 @@ WARN_LOGFILE = # spaces. # Note: If this tag is empty the current directory is searched. -INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include/ @CMAKE_CURRENT_SOURCE_DIR@/lib/ @CMAKE_CURRENT_SOURCE_DIR@/docs +INPUT = @CMAKE_CURRENT_SOURCE_DIR@/include/ \ + @CMAKE_CURRENT_SOURCE_DIR@/docs # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -794,7 +794,7 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = +EXCLUDE_PATTERNS = */external/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -805,7 +805,7 @@ EXCLUDE_PATTERNS = # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* -EXCLUDE_SYMBOLS = +EXCLUDE_SYMBOLS = *::detail::* # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include @@ -905,7 +905,7 @@ INLINE_SOURCES = NO # Fortran comments will always remain visible. # The default value is: YES. -STRIP_CODE_COMMENTS = YES +STRIP_CODE_COMMENTS = NO # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. @@ -917,7 +917,7 @@ REFERENCED_BY_RELATION = YES # all documented entities called/used by that function will be listed. # The default value is: NO. -REFERENCES_RELATION = NO +REFERENCES_RELATION = YES # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES, then the hyperlinks from functions in REFERENCES_RELATION and @@ -1407,7 +1407,7 @@ FORMULA_TRANSPARENT = YES # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -USE_MATHJAX = NO +USE_MATHJAX = YES # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: @@ -1521,7 +1521,7 @@ SEARCHDATA_FILE = searchdata.xml # projects and redirect the results back to the right project. # This tag requires that the tag SEARCHENGINE is set to YES. -EXTERNAL_SEARCH_ID = +EXTERNAL_SEARCH_ID = phasar # The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen # projects other than the one defined by this configuration file, but that are @@ -1540,7 +1540,7 @@ EXTRA_SEARCH_MAPPINGS = # If the GENERATE_LATEX tag is set to YES doxygen will generate LaTeX output. # The default value is: YES. -GENERATE_LATEX = YES +GENERATE_LATEX = NO # The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -1917,7 +1917,7 @@ SEARCH_INCLUDES = YES # preprocessor. # This tag requires that the tag SEARCH_INCLUDES is set to YES. -INCLUDE_PATH = +INCLUDE_PATH = @PHASAR_BINARY_DIR@/include # You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard # patterns (like *.h and *.hpp) to filter out the header-files in the @@ -2040,7 +2040,7 @@ DIA_PATH = # and usage relations if the target is undocumented or is not a class. # The default value is: YES. -HIDE_UNDOC_RELATIONS = YES +HIDE_UNDOC_RELATIONS = NO # If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is # available from the path. This tool is part of Graphviz (see: @@ -2100,7 +2100,7 @@ CLASS_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -COLLABORATION_GRAPH = YES +COLLABORATION_GRAPH = NO # If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for # groups, showing the direct groups dependencies. @@ -2136,7 +2136,7 @@ UML_LIMIT_NUM_FIELDS = 10 # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -TEMPLATE_RELATIONS = NO +TEMPLATE_RELATIONS = YES # If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to # YES then doxygen will generate a graph for each documented file showing the @@ -2145,7 +2145,7 @@ TEMPLATE_RELATIONS = NO # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDE_GRAPH = YES +INCLUDE_GRAPH = NO # If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are # set to YES then doxygen will generate a graph for each documented file showing @@ -2154,7 +2154,7 @@ INCLUDE_GRAPH = YES # The default value is: YES. # This tag requires that the tag HAVE_DOT is set to YES. -INCLUDED_BY_GRAPH = YES +INCLUDED_BY_GRAPH = NO # If the CALL_GRAPH tag is set to YES then doxygen will generate a call # dependency graph for every global function or class method. @@ -2203,7 +2203,7 @@ DIRECTORY_GRAPH = YES # The default value is: png. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_IMAGE_FORMAT = png +DOT_IMAGE_FORMAT = svg # If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to # enable generation of interactive SVG images that allow zooming and panning. @@ -2276,7 +2276,7 @@ MAX_DOT_GRAPH_DEPTH = 0 # The default value is: NO. # This tag requires that the tag HAVE_DOT is set to YES. -DOT_TRANSPARENT = NO +DOT_TRANSPARENT = YES # Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output # files in one run (i.e. multiple -o and -T options on the command line). This diff --git a/docs/README.dox b/docs/README.dox index e00ef59771..6ae3b786c0 100644 --- a/docs/README.dox +++ b/docs/README.dox @@ -2,14 +2,27 @@ @mainpage PhASAR: A LLVM-based Static Analysis Framework -@author Philipp Schubert (E-Mail: philipp.schubert@upb.de) and others +PhASAR is a LLVM-based static analysis framework written in C++. It allows users to specify arbitrary data-flow problems which are then solved in a fully-automated manner on the specified LLVM IR target code. Computing points-to information, call-graph(s), etc. is done by the framework, thus you can focus on what matters. -\b Copyright \n - Copyright 2017 Philipp Schubert. All rights reserved. +This page contains the generated code documentation of PhASAR. +You can find the original source on GitHub: . The README should already give you a good first overview. -\b License \n - See LICENSE.txt +For further information, please checkout PhASAR's [Wiki](https://github.com/secure-software-engineering/phasar/wiki). + +@subsubsection SSEG Secure Software Engineering Group + +PhASAR is primarily developed and maintained by the [Secure Software Engineering Group](https://www.hni.uni-paderborn.de/sse) at Heinz Nixdorf Institute (University of Paderborn) and [Fraunhofer IEM](https://www.iem.fraunhofer.de/). -TODO: add detailed description. +PhASAR was initially developed by Philipp Dominik Schubert (@pdschubert)(). + +\b Currently, PhASAR is maintained by +- Fabian Schiebel (@fabianbs96)(fabian.schiebel@iem.fraunhofer.de) +- Sriteja Kummita (@sritejakv) +- Lucas Briese (@jusito) +- Martin Mory (@MMory)(martin.mory@upb.de) +- *others* + +\b License \n + PhASAR is made available under the permissive MIT License. See LICENSE.txt */ diff --git a/include/phasar/AnalysisStrategy/AnalysisSetup.h b/include/phasar/AnalysisStrategy/AnalysisSetup.h index 975ed1c292..7b40140d4c 100644 --- a/include/phasar/AnalysisStrategy/AnalysisSetup.h +++ b/include/phasar/AnalysisStrategy/AnalysisSetup.h @@ -16,7 +16,7 @@ namespace psr { -// Indicates that an analysis does not need a special configuration (file). +/// Indicates that an analysis does not need a special configuration (file). struct HasNoConfigurationType {}; struct AnalysisSetup { diff --git a/include/phasar/ControlFlow/CallGraphData.h b/include/phasar/ControlFlow/CallGraphData.h index ed50d66e42..58393936a9 100644 --- a/include/phasar/ControlFlow/CallGraphData.h +++ b/include/phasar/ControlFlow/CallGraphData.h @@ -18,8 +18,12 @@ #include namespace psr { + +/// A data structure used for storing, serializing and deserializing call-graph +/// information. struct CallGraphData { - // Mangled FunName --> [CS-IDs] + + /// Mangled FunName --> [CS-IDs] std::unordered_map> FToFunctionVertexTy{}; CallGraphData() noexcept = default; diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index 71f340462c..4b1ceab7b4 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -36,7 +36,7 @@ template class ICFGBase : public CRTPBase { } /// returns the function definition or declaration with the given name. If - /// ther eis no such function, returns a default constructed f_t (nullptr for + /// there is no such function, returns a default constructed f_t (nullptr for /// pointers). [[nodiscard]] f_t getFunction(llvm::StringRef Fun) const { return self().getFunctionImpl(Fun); diff --git a/include/phasar/DB/ProjectIRDBBase.h b/include/phasar/DB/ProjectIRDBBase.h index 092019686d..8b68b81910 100644 --- a/include/phasar/DB/ProjectIRDBBase.h +++ b/include/phasar/DB/ProjectIRDBBase.h @@ -24,7 +24,7 @@ template struct ProjectIRDBTraits { // using g_t }; -/// This class owns the IR code of the project under analysis and some +/// This class owns the IR code of the target being analyzed and some /// very important information associated with the IR. /// When an object of this class is destroyed it will clean up all IR related /// stuff that is stored in it. diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 4a74d9e3ff..ca6c0ad706 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -65,7 +65,7 @@ concept IsEdgeFunction = requires(const T &EF, const EdgeFunction std::same_as>; {T::join(CEF, TEEF)} -> std::same_as>; }; - // clang-format on +// clang-format on #endif @@ -78,10 +78,10 @@ enum class EdgeFunctionAllocationPolicy { class EdgeFunctionBase { public: template - static constexpr bool - IsSOOCandidate = sizeof(ConcreteEF) <= sizeof(void *) && // NOLINT - alignof(ConcreteEF) <= alignof(void *) && - std::is_trivially_copyable_v; + static constexpr bool IsSOOCandidate = + sizeof(ConcreteEF) <= sizeof(void *) && // NOLINT + alignof(ConcreteEF) <= alignof(void *) && + std::is_trivially_copyable_v; using AllocationPolicy = EdgeFunctionAllocationPolicy; @@ -89,7 +89,9 @@ class EdgeFunctionBase { struct RefCountedBase { mutable std::atomic_size_t Rc = 0; }; - template struct RefCounted : RefCountedBase { T Value; }; + template struct RefCounted : RefCountedBase { + T Value; + }; template struct CachedRefCounted : RefCounted { EdgeFunctionSingletonCache *Cache{}; @@ -118,8 +120,8 @@ class EdgeFunctionBase { : AllocationPolicy::CustomHeapAllocated; }; -/// Non-null reference to an edge function that is guarenteed to be managed by -/// an EdgeFunction object. +/// \brief Non-null reference to an edge function that is guarenteed to be +/// managed by an EdgeFunction object. template class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase { template friend class EdgeFunction; @@ -164,8 +166,8 @@ class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase { IsCached{}; }; -/// Ref-counted and type-erased edge function with small-object optimization. -/// Supports caching. +/// \brief Ref-counted and type-erased edge function with small-object +/// optimization. Supports caching. template // -- combined copy and move assignment // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) @@ -265,8 +267,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { explicit EdgeFunction( std::in_place_type_t /*unused*/, ArgTys &&...Args) noexcept(IsSOOCandidate> && - std::is_nothrow_constructible_v) + std::is_nothrow_constructible_v) : EdgeFunction( [](auto &&...Args) { if constexpr (IsSOOCandidate>) { diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h index 3b90341336..e6b4cd2e17 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h @@ -42,222 +42,225 @@ template class EdgeFunctions { virtual ~EdgeFunctions() = default; - // - // 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()): - // - // 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 - // - // The above flow-function implementation corresponds to the following edges - // in the exploded supergraph. - // - // 0 o ... - // | |\ ... - // Curr := x = instruction_1 o p | | \ ... - // | | | ... - // v v v ... - // 0 o x ... - // - // Succ := y = instruction_2 q r - // - // 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: - // - // getNormalEdgeFunction(0, Curr, 0 Succ); - // getNormalEdgeFunction(o, Curr, o Succ); - // getNormalEdgeFunction(o, Curr, x Succ); - // + /// + /// 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 virtual EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, d_t SuccNode) = 0; - // - // 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()): - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o p ... - // \ \ \ ... - // CallInst := x = CalleeFun(o, p, ...) \ \ +----------------+ - // \ +---------------- | - // +-------------+ + | - // ... | | | - // ... | | | - // 0 o p ... | | | - // | | | - // | | | - // | | | - // Ty CalleeFun(q, r, ...) | | | - // v v v - // 0 q r ... - // - // start point - // - // 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: - // - // getCallEdgeFunction(CallInst, 0, CalleeFun, 0); - // getCallEdgeFunction(CallInst, o, CalleeFun, q); - // getCallEdgeFunction(CallInst, p, CalleeFun, r); - // + /// + /// 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 virtual EdgeFunction getCallEdgeFunction(n_t CallInst, d_t SrcNode, f_t CalleeFun, d_t DestNode) = 0; - // - // 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()): - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o ... - // - // CallSite = RetSite := x = CalleeFun(o, ...) - // +------------------+ - // +--|---------------+ | - // +--|--|------------+ | | - // v v v ... | | | - // 0 o x ... | | | - // | | | - // | | | - // | | | - // Ty CalleeFun(q, ...) | | | - // | | | - // 0 q r - // - // ExitInst := return r - // - // 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: - // - // getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, 0, RetSite, 0); - // getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, q, RetSite, o); - // getReturnEdgeFunction(CallSite, CalleeFun, ExitInst, r, RetSite, x); - // + /// + /// 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 virtual EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, d_t ExitNode, n_t RetSite, d_t RetNode) = 0; - // - // 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()): - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o ... - // | | - // | +-------+ - // +--------+ | - // | | - // CallSite = RetSite := x = CalleeFun(o, p, ...) | | - // | | - // +--------+ | - // | +-------+ - // v v - // 0 o x ... - // - // 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}); - // + /// + /// 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}); + /// virtual EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, d_t RetSiteNode, llvm::ArrayRef Callees) = 0; - // - // 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. - // + /// + /// 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. + /// virtual EdgeFunction getSummaryEdgeFunction(n_t /*Curr*/, d_t /*CurrNode*/, n_t /*Succ*/, diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index 2f3fa53f5c..6e54fe3936 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -36,9 +36,9 @@ namespace psr { // FlowFunction Class //===----------------------------------------------------------------------===// -// -// This class models a flow function for distributive data-flow problems. -// +/// +/// This class models a flow function for distributive data-flow problems. +/// template > class FlowFunction { static_assert(std::is_same::value, "Container values needs to be the same as D"); @@ -52,17 +52,17 @@ template > class FlowFunction { virtual ~FlowFunction() = default; - // - // This function is called for each data-flow fact Source that holds before - // the instruction under analysis. The return value is a (potentially empty) - // set of data-flow facts that are generated from Source and hold after the - // instruction under analysis. In other words: the function describes what - // exploded supergraph edges have to be "drawn". - // - // Please also refer to the various flow function factories of the - // FlowFunctions interface: FlowFunctions::get*FlowFunction() for more - // details. - // + /// + /// This function is called for each data-flow fact Source that holds before + /// the instruction under analysis. The return value is a (potentially empty) + /// set of data-flow facts that are generated from Source and hold after the + /// instruction under analysis. In other words: the function describes what + /// exploded supergraph edges have to be "drawn". + /// + /// Please also refer to the various flow function factories of the + /// FlowFunctions interface: FlowFunctions::get*FlowFunction() for more + /// details. + /// virtual container_type computeTargets(D Source) = 0; }; @@ -153,13 +153,13 @@ template class FlowFunctionTemplates { /// dataflow-facts x, f(x) = {x}. /// /// In the exploded supergraph it may look as follows: - /// + /// \code /// x1 x1 x3 ... /// | | | ... /// id-instruction | | | ... /// v v v ... /// x1 x2 x3 ... - /// + /// \endcode static auto identityFlow() { struct IdFF final : public FlowFunction { container_type computeTargets(d_t Source) override { @@ -178,14 +178,14 @@ template class FlowFunctionTemplates { /// dataflow-facts x, f(x) = F(x). /// /// In the exploded supergraph it may look as follows: - /// + /// \code /// x /// | /// inst F /// / / | \ \ ... /// v v v v v /// x1 x2 x x3 x4 - /// + /// \endcode template static auto lambdaFlow(Fn &&F) { struct LambdaFlow final : public FlowFunction { LambdaFlow(Fn &&F) : Flow(std::forward(F)) {} @@ -208,21 +208,22 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = generateFlow(v, w), then for all incoming /// dataflow facts x: + /// \code /// f(w) = {v, w}, /// f(x) = {x}. - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// x w u ... /// | |\ | ... /// inst | | \ | ... /// v v v v ... /// x w v u - /// + /// \endcode /// \note If the FactToGenerate already holds at the beginning of the /// statement, this flow function does not kill it. For IFDS analysis it makes /// no difference, but in the case of IDE, the corresponding edge functions - /// are being joined together potentially lowing precition. If that is an + /// are being joined together potentially lowering precision. If that is an /// issue, use transferFlow instead. static auto generateFlow(d_t FactToGenerate, d_t From) { struct GenFrom final : public FlowFunction { @@ -250,9 +251,10 @@ template class FlowFunctionTemplates { /// /// So, given a flow function f = generateFlowIf(v, p), for all incoming /// dataflow facts x: + /// \code /// f(x) = {v, x} if p(x) == true /// f(x) = {x} else. - /// + /// \endcode template >> static auto generateFlowIf(d_t FactToGenerate, Fn Predicate) { @@ -281,17 +283,18 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = generateManyFlows({v1, v2, ..., vN}, w), for all /// incoming dataflow facts x: + /// \code /// f(w) = {v1, v2, ..., vN, w} /// f(x) = {x}. - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// x w u ... /// | |\ \ ... \ | ... /// inst | | \ \ ... \ | ... /// v v v v ... \ v ... /// x w v1 v2 ... vN u - /// + /// \endcode template , typename = std::enable_if_t>> static auto generateManyFlows(Range &&FactsToGenerate, d_t From) { @@ -324,17 +327,18 @@ template class FlowFunctionTemplates { /// (FactToKill). /// /// Given a flow function f = killFlow(v), for all incoming dataflow facts x: + /// \code /// f(v) = {} /// f(x) = {x} - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// u v w ... /// | | | /// inst | | /// v v /// u v w ... - /// + /// \endcode static auto killFlow(d_t FactToKill) { struct KillFlow final : public FlowFunction { KillFlow(d_t KillValue) : KillValue(std::move(KillValue)) {} @@ -355,9 +359,10 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = killFlowIf(p), for all incoming dataflow facts /// x: + /// \code /// f(x) = {} if p(x) == true /// f(x) = {x} else. - /// + /// \endcode template >> static auto killFlowIf(Fn Predicate) { @@ -382,20 +387,21 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = killManyFlows({v1, v2, ..., vN}), for all /// incoming dataflow facts x: + /// \code /// f(v1) = {} /// f(v2) = {} /// ... /// f(vN) = {} /// f(x) = {x}. - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// u v1 v2 ... vN w ... /// | | | | | /// inst | | /// v v /// u v1 v2 ... vN w ... - /// + /// \endcode template , typename = std::enable_if_t>> static auto killManyFlows(Range &&FactsToKill) { @@ -419,8 +425,9 @@ template class FlowFunctionTemplates { /// A flow function that stops propagating *all* incoming dataflow facts. /// /// Given a flow function f = killAllFlows(), for all incoming dataflow facts + /// \code /// x, f(x) = {}. - /// + /// \endcode static auto killAllFlows() { struct KillAllFF final : public FlowFunction { Container computeTargets(d_t /*Source*/) override { return Container(); } @@ -440,20 +447,21 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = generateFlowAndKillAllOthers(v, w), for all /// incoming dataflow facts x: + /// \code /// f(w) = {v, w} /// f(x) = {}. - /// + /// \endcode /// Equivalent to: killFlowIf(λz.z!=w) o generateFlow(v, w) (where o denotes /// function composition) /// /// In the exploded supergraph it may look as follows: - /// + /// \code /// x w u ... /// | |\ | /// inst | \ ... /// v v /// x w v u - /// + /// \endcode static auto generateFlowAndKillAllOthers(d_t FactToGenerate, d_t From) { struct GenFlowAndKillAllOthers final : public FlowFunction { @@ -481,17 +489,18 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = generateManyFlowsAndKillAllOthers({v1, v2, ..., /// vN}, w), for all incoming dataflow facts x: + /// \code /// f(w) = {v1, v2, ..., vN, w} /// f(x) = {}. - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// x w u ... /// | |\ \ ... \ | ... /// inst | \ \ ... \ ... /// v v v ... \ ... /// x w v1 v2 ... vN u - /// + /// \endcode template , typename = std::enable_if_t>> static auto generateManyFlowsAndKillAllOthers(Range &&FactsToGenerate, @@ -533,19 +542,20 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = transferFlow(v, w), for all incoming dataflow /// facts x: + /// \code /// f(v) = {} /// f(w) = {v, w} /// f(x) = {x}. - /// + /// \endcode /// In the exploded supergraph it may look as follows: - /// + /// \code /// x w v u ... /// | |\ | | ... /// | | \ | ... /// inst | | \ | ... /// v v v v ... /// x w v u - /// + /// \endcode static auto transferFlow(d_t FactToGenerate, d_t From) { struct TransferFlow final : public FlowFunction { TransferFlow(d_t GenValue, d_t FromValue) @@ -575,8 +585,9 @@ template class FlowFunctionTemplates { /// /// Given a flow function f = unionFlows(g, h), for all incoming dataflow /// facts x: + /// \code /// f(x) = g(x) u h(x). (where u denotes set-union) - /// + /// \endcode template && @@ -635,242 +646,239 @@ class FlowFunctions virtual ~FlowFunctions() = default; - // - // 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. - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o ... - // | |\ ... - // x = instruction_1 o p | | \ ... - // | | | ... - // v v v ... - // 0 o x ... - // - // y = instruction_2 q r - // + /// + /// 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 virtual FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) = 0; - // - // 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. - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o p ... - // \ \ \ ... - // x = CalleeFun(o, p, ...) \ \ +----------------+ - // \ +---------------- | - // +-------------+ + | - // ... | | | - // ... | | | - // 0 o p ... | | | - // | | | - // | | | - // | | | - // Ty CalleeFun(q, r, ...) | | | - // v v v - // 0 q r ... - // - // start point - // + /// + /// 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 virtual FlowFunctionPtrType getCallFlowFunction(n_t CallInst, f_t CalleeFun) = 0; - // - // 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. - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o ... - // - // x = CalleeFun(o, ...) - // +------------------+ - // +--|---------------+ | - // +--|--|------------+ | | - // v v v ... | | | - // 0 o x ... | | | - // | | | - // | | | - // | | | - // Ty CalleeFun(q, ...) | | | - // | | | - // 0 q r ... - // - // return r - // + /// + /// 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 virtual FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) = 0; - // 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 + /// 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 virtual void applyUnbalancedRetFlowFunctionSideEffects(f_t CalleeFun, n_t ExitInst, d_t Source) { // By default, do nothing } - // - // 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. - // - // 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 - // - // The above implementation corresponds to the following edges in the exploded - // supergraph. - // - // 0 o ... - // | | - // | +-------+ - // +--------+ | - // | | - // x = CalleeFun(o, p, ...) | | - // | | - // +--------+ | - // | +-------+ - // v v - // 0 o x ... - // + /// + /// 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 virtual FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, llvm::ArrayRef Callees) = 0; - // - // 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. - // + /// + /// 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. + /// virtual FlowFunctionPtrType getSummaryFlowFunction(n_t /*Curr*/, f_t /*CalleeFun*/) { return nullptr; } }; -} // namespace psr +} // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h index 57120a4398..46e1cee632 100644 --- a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h @@ -53,6 +53,12 @@ class AllTopFnProvider< } }; +/// \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. +/// +/// For more information on how to write an IDE analysis, see [Writing an IDE +/// Analysis](https://github.com/secure-software-engineering/phasar/wiki/Writing-an-IDE-analysis) template > class IDETabulationProblem : public FlowFunctions, @@ -73,6 +79,17 @@ class IDETabulationProblem : public FlowFunctions, using ConfigurationTy = HasNoConfigurationType; + /// Takes an IR database (IRDB) and collects information from it to create a + /// tabulation problem. + /// @param[in] IRDB The project IR database, that holds the code under + /// analysis + /// @param[in] EntryPoints The (mangled) names of all entry functions of the + /// target being analyzed, given as a vector of strings. An example would + /// simply be `{"main"}`. To set every function as entry point, pass + /// `"__ALL__"` + /// @param[in] ZeroValue Provides the special tautological zero value (aka. + /// Λ). If not provided here, you must set it via \link initializeZeroValue() + /// \endlink. explicit IDETabulationProblem( const ProjectIRDBBase *IRDB, std::vector EntryPoints, std::optional diff --git a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h index 71dcba326d..8f2046fcb0 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h @@ -39,22 +39,40 @@ enum class SolverConfigOptions : uint32_t { All = ~0U }; +/// \brief Configuration options for the solving process of IFDS/IDE problems struct IFDSIDESolverConfig { IFDSIDESolverConfig() noexcept = default; IFDSIDESolverConfig(SolverConfigOptions Options) noexcept; + /// Returns whether the solver should handle unbalanced returns (default: + /// false) [[nodiscard]] bool followReturnsPastSeeds() const; + /// Returns whether the solver should automatically insert an identityFlow + /// propagation for the special zero value (default: true) [[nodiscard]] bool autoAddZero() const; + /// 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; + /// 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; + /// Returns, whether the solver should emit the ESG as DOT graph on the + /// command-line (default: false) [[nodiscard]] bool emitESG() const; + /// Currently unused [[nodiscard]] bool computePersistedSummaries() const; + /// \see followReturnsPastSeeds void setFollowReturnsPastSeeds(bool Set = true); + /// \see autoAddZero void setAutoAddZero(bool Set = true); + /// \see computeValues void setComputeValues(bool Set = true); + /// \see recordEdges void setRecordEdges(bool Set = true); + /// \see emitESG void setEmitESG(bool Set = true); + /// \see computePersistedSummaries void setComputePersistedSummaries(bool Set = true); void setConfig(SolverConfigOptions Opt); diff --git a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h index cbafa68c3a..2f884bd842 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h @@ -19,6 +19,12 @@ namespace psr { +/// \brief The analysis problem interface for IFDS problems (solvable by the +/// IFDSSolver). Create a subclass from this and override all pure-virtual +/// functions to create your own IFDS analysis. +/// +/// For more information on how to write an IFDS analysis, see [Writing an IFDS +/// Analysis](https://github.com/secure-software-engineering/phasar/wiki/Writing-an-IFDS-analysis) template > class IFDSTabulationProblem @@ -38,6 +44,17 @@ class IFDSTabulationProblem using typename Base::t_t; using typename Base::v_t; + /// Takes an IR database (IRDB) and collects information from it to create a + /// tabulation problem. + /// @param[in] IRDB The project IR database, that holds the code under + /// analysis + /// @param[in] EntryPoints The (mangled) names of all entry functions of the + /// target being analyzed, given as a vector of strings. An example would + /// simply be `{"main"}`. To set every function as entry point, pass + /// `"__ALL__"` + /// @param[in] ZeroValue Provides the special tautological zero value (aka. + /// Λ). + /// \endlink. explicit IFDSTabulationProblem(const ProjectIRDBBase *IRDB, std::vector EntryPoints, d_t ZeroValue) diff --git a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h index db7ea654e7..fa2f86fe14 100644 --- a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h +++ b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h @@ -22,6 +22,9 @@ namespace psr { +/// \brief Represent the starting points of the analysis. +/// +/// The initial facts that should hold at the entry points. template class InitialSeeds { public: using GeneralizedSeeds = std::map>; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h index 7d357a0a05..d0f9472d9c 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h @@ -18,6 +18,10 @@ namespace psr { template class Compressor; +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that can be efficiently passed by value template class Compressor>> { public: @@ -61,6 +65,10 @@ class Compressor>> { llvm::SmallVector FromInt; }; +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that cannot be efficiently passed by value template class Compressor>> { public: @@ -69,6 +77,9 @@ class Compressor>> { ToInt.reserve(Capacity); } + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. uint32_t getOrInsert(const T &Elem) { if (auto It = ToInt.find(&Elem); It != ToInt.end()) { return It->second; @@ -79,6 +90,9 @@ class Compressor>> { return Ret; } + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. uint32_t getOrInsert(T &&Elem) { if (auto It = ToInt.find(&Elem); It != ToInt.end()) { return It->second; @@ -89,6 +103,8 @@ class Compressor>> { return Ret; } + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present, std::nullopt will be returned std::optional getOrNull(const T &Elem) const { if (auto It = ToInt.find(&Elem); It != ToInt.end()) { return It->second; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h index a0f42d821f..f6c0c3a5db 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h @@ -137,6 +137,10 @@ template struct FlowFunctionCacheBase { } // namespace detail +/// This class caches flow and edge functions to avoid their reconstruction. +/// When a flow or edge function must be applied multiple times, a cached +/// version is used if it exists, otherwise a new one is created and inserted +/// into the cache. This class is used within both IDE solver implementations. template class FlowFunctionCache : detail::FlowFunctionCacheBase, diff --git a/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h index 744c8e7e38..20ff8b6c3e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h @@ -21,7 +21,7 @@ namespace psr { /// XXX (#734): When upgrading to C++20, create a concept checking valid /// SolverResults types -/// A type-erased version of the main functionality of SolverResults. +/// \brief A type-erased version of the main functionality of SolverResults. /// Can be accepted by consumers that don't need deep access to the internals /// (so, the usual ones). As we have now two kinds of solver-results /// (SolverResults and IdBasedSolverResults), we need a common way of accessing diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index e6423bab0e..cfebf428e3 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -27,6 +27,14 @@ namespace psr { +/// \brief Solves the given IFDSTabulationProblem as described in the 1995 paper +/// by Reps, Horwitz and Sagiv. To solve the problem, call solve(). Results can +/// then be queried by using resultAt() and resultsAt(). +/// +/// \note PhASAR implements IFDS in terms of IDE, so in case you do not need the +/// raw SolverResults, for maximum performance you should use +/// IFDSIDESolverConfig#setComputeValues(bool) to disable IDE's +/// phase 2. template > class IFDSSolver diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h index bab1509d0d..68ee40cb80 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -50,7 +50,7 @@ namespace psr { /// This solver implements the optimizations and the $JF_N$ layout from the /// paper "Scaling Interprocedural Static Data-Flow Analysis to Large C/C++ /// Applications: An Experience Report" -/// (https://doi.org/10.4230/LIPIcs.ECOOP.2024.36) by Schiebel, Sattler, +/// () by Schiebel, Sattler, /// Schubert, Apel, and Bodden. template > diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 664d0e111d..51b8b3ce9d 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -46,11 +46,22 @@ class SolverResultsBase { using d_t = D; using l_t = L; + /// Returns the result that the IDE analysis computed for the fact Node right + /// after the statement Stmt. + /// + /// A default-constructed l_t, if no analysis result was computed at this + /// point. [[nodiscard]] ByConstRef resultAt(ByConstRef Stmt, ByConstRef Node) const { return self().Results.get(Stmt, Node); } + /// Returns the results that the IDE analysis computed right after the + /// statement Stmt. + /// + /// \param Stmt The statement, where the analysis results are requested + /// \param StripZero Whether the special zero value should be stripped from + /// the result. [[nodiscard]] std::unordered_map resultsAt(ByConstRef Stmt, bool StripZero) const { std::unordered_map Result = self().Results.row(Stmt); @@ -60,19 +71,26 @@ class SolverResultsBase { return Result; } + /// Returns the results that the IDE analysis computed right after the + /// statement Stmt. + /// + /// Does not strip the special zero value from the result. [[nodiscard]] const std::unordered_map & resultsAt(ByConstRef Stmt) const { return self().Results.row(Stmt); } + /// The internal representation of this SolverResults object. [[nodiscard]] const auto &rowMapView() const { return self().Results.rowMapView(); } + /// Whether the analysis has computed any results for the statement Stmt. [[nodiscard]] bool containsNode(ByConstRef Stmt) const { return self().Results.containsRow(Stmt); } + /// Similar to resultsAt(ByConstRef). [[nodiscard]] const auto &row(ByConstRef Stmt) const { return self().Results.row(Stmt); } @@ -247,12 +265,12 @@ class OwningSolverResults D ZV) noexcept(std::is_nothrow_move_constructible_v) : Results(std::move(ResTab)), ZV(std::move(ZV)) {} - [[nodiscard]] SolverResults get() const &noexcept { + [[nodiscard]] SolverResults get() const & noexcept { return {Results, ZV}; } SolverResults get() && = delete; - [[nodiscard]] operator SolverResults() const &noexcept { + [[nodiscard]] operator SolverResults() const & noexcept { return get(); } diff --git a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h index 41cb4d58e1..f7cbd0cfc6 100644 --- a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h +++ b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h @@ -45,9 +45,9 @@ class [[deprecated("This ancient API is not maintained and should not be used " std::map> SpecialEdgeFunctions; std::vector SpecialFunctionNames; - // Constructs the SpecialSummaryMap such that it contains all glibc, - // llvm.intrinsics and C++'s new, new[], delete, delete[] with identity - // flow functions. + /// Constructs the SpecialSummaryMap such that it contains all glibc, + /// llvm.intrinsics and C++'s new, new[], delete, delete[] with identity + /// flow functions. SpecialSummaries() { // insert default flow and edge functions for (const auto &FunctionName : @@ -71,7 +71,7 @@ class [[deprecated("This ancient API is not maintained and should not be used " return Instance; } - // Returns true, when an existing function is overwritten, false otherwise. + /// Returns true, when an existing function is overwritten, false otherwise. bool provideSpecialSummary(const std::string &Name, FlowFunctionPtrType FlowFunc) { bool Override = containsSpecialSummary(Name); @@ -79,7 +79,7 @@ class [[deprecated("This ancient API is not maintained and should not be used " return Override; } - // Returns true, when an existing function is overwritten, false otherwise. + /// Returns true, when an existing function is overwritten, false otherwise. bool provideSpecialSummary(const std::string &Name, FlowFunctionPtrType FlowFunc, std::shared_ptr> EdgeFunc) { @@ -97,8 +97,8 @@ class [[deprecated("This ancient API is not maintained and should not be used " return SpecialFlowFunctions.count(Name); } - FlowFunctionPtrType getSpecialFlowFunctionSummary( - const llvm::Function *Func) { + FlowFunctionPtrType + getSpecialFlowFunctionSummary(const llvm::Function *Func) { return getSpecialFlowFunctionSummary(Func->getName()); } @@ -106,18 +106,18 @@ class [[deprecated("This ancient API is not maintained and should not be used " return SpecialFlowFunctions[Name]; } - std::shared_ptr> getSpecialEdgeFunctionSummary( - const llvm::Function *Func) { + std::shared_ptr> + getSpecialEdgeFunctionSummary(const llvm::Function *Func) { return getSpecialEdgeFunctionSummary(Func->getName()); } - std::shared_ptr> getSpecialEdgeFunctionSummary( - const std::string &Name) { + std::shared_ptr> + getSpecialEdgeFunctionSummary(const std::string &Name) { return SpecialEdgeFunctions[Name]; } - friend llvm::raw_ostream &operator<<( - llvm::raw_ostream &OS, const SpecialSummaries &SpecialSumms) { + friend llvm::raw_ostream & + operator<<(llvm::raw_ostream &OS, const SpecialSummaries &SpecialSumms) { OS << "SpecialSummaries:\n"; for (auto &Entry : SpecialSumms.SpecialFunctionNames) { OS << Entry.first << " "; diff --git a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h index 098c4ff217..c7326729b8 100644 --- a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h +++ b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h @@ -13,6 +13,10 @@ namespace psr { +/// Stores a call-string context that can be used in interprocedural monotone +/// analysis to achieve (limited) context sensitivity. +/// @tparam N Type of the call-string elements. +/// @tparam K Maximal length the call string can have. template class CallStringCTX { protected: std::deque CallString; diff --git a/include/phasar/DataFlow/Mono/InterMonoProblem.h b/include/phasar/DataFlow/Mono/InterMonoProblem.h index 9ee1b3298f..326cf9df91 100644 --- a/include/phasar/DataFlow/Mono/InterMonoProblem.h +++ b/include/phasar/DataFlow/Mono/InterMonoProblem.h @@ -31,6 +31,9 @@ namespace psr { template class TypeHierarchy; template class ICFG; +/// \brief The analysis problem interface for interprocedural monotone problems +/// (solvable by the InterMonoSolver). Create a subclass from this and override +/// all pure-virtual functions to create your own inter-mono analysis. template class InterMonoProblem : public IntraMonoProblem { public: @@ -47,6 +50,14 @@ class InterMonoProblem : public IntraMonoProblem { const i_t *ICF; public: + /// An interprocedural monotone problem generated from an intermediate + /// representation, a type hierarchy of said representation, a control flow + /// graph, points-to information and optionally a vector of entry points. + /// @param[in] IRDB A project IR database. + /// @param[in] TH A type hierarchy based on the given IRDB. + /// @param[in] CF A control flow graph based on the given IRDB. + /// @param[in] PT Points-to information based on the given IRDB. + /// @param[in] EntryPoints A vector of entry points. Provide at least one. InterMonoProblem(const ProjectIRDBBase *IRDB, const TypeHierarchy *TH, const i_t *ICF, AliasInfoRef PT, diff --git a/include/phasar/DataFlow/Mono/IntraMonoProblem.h b/include/phasar/DataFlow/Mono/IntraMonoProblem.h index 4915cfe4f9..954d2913a8 100644 --- a/include/phasar/DataFlow/Mono/IntraMonoProblem.h +++ b/include/phasar/DataFlow/Mono/IntraMonoProblem.h @@ -35,6 +35,9 @@ struct HasNoConfigurationType; template class TypeHierarchy; template class CFG; +/// \brief The analysis problem interface for intraprocedural monotone problems +/// (solvable by the IntraMonoSolver). Create a subclass from this and override +/// all pure-virtual functions to create your own mono analysis. template class IntraMonoProblem { public: using n_t = typename AnalysisDomainTy::n_t; @@ -58,10 +61,16 @@ template class IntraMonoProblem { [[maybe_unused]] Soundness S = Soundness::Soundy; public: - // denote that a problem does not require a configuration (type/file) - // a user problem can override the type of configuration to be used, if any using ConfigurationTy = HasNoConfigurationType; + /// An intraprocedural monotone problem generated from an intermediate + /// representation, a type hierarchy of said representation, a control flow + /// graph, points-to information and optionally a vector of entry points. + /// @param[in] IRDB A project IR database. + /// @param[in] TH A type hierarchy based on the given IRDB. + /// @param[in] CF A control flow graph based on the given IRDB. + /// @param[in] PT Points-to information based on the given IRDB. + /// @param[in] EntryPoints A vector of entry points. Provide at least one. IntraMonoProblem(const ProjectIRDBBase *IRDB, const TypeHierarchy *TH, const CFGBase *CF, AliasInfoRef PT, diff --git a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h index 0be77db3d3..b0abf88354 100644 --- a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h @@ -27,6 +27,12 @@ namespace psr { +/// \brief A solver class for interprocedual monotone problems (derived from +/// InterMonoProblem). To solve the problem, call solve(). +/// +/// \tparam AnalysisDomainTy type of the analysis domain. +/// \tparam K An unsigned integer used as the maximum length for call-string +/// contexts. template class InterMonoSolver { public: using ProblemTy = InterMonoProblem; diff --git a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h index 739b6fa301..ed5b1f449d 100644 --- a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h @@ -29,6 +29,10 @@ namespace psr { +/// \brief A solver class for intraprocedual monotone problems. To solve the +/// problem, call solve(). +/// +/// \tparam AnalysisDomainTy type of the analysis domain. template class IntraMonoSolver { public: using ProblemTy = IntraMonoProblem; diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h index d781d127f5..b973932313 100644 --- a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h @@ -28,6 +28,8 @@ namespace psr { +/// \brief A utility class that allows path-reconstruction for IFDS/IDE solver +/// results. template class PathSensitivityManager : public PathSensitivityManagerBase, diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h index c92c35aae3..74ed9e1c51 100644 --- a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h @@ -67,6 +67,11 @@ class PathSensitivityManagerMixin { } public: + /// Reconstruct the combined control- and data-flow paths the lead to any of + /// the given data-flow facts in FactsRange holding right after Inst. + /// + /// The result is given as graph, where cycles are unrolled once in an + /// implementation-defined way. template < typename FactsRangeTy, typename ConfigTy, typename Filter = DefaultPathTracingFilter, @@ -147,6 +152,11 @@ class PathSensitivityManagerMixin { return Dag; } + /// Reconstruct the combined control- and data-flow paths the lead to any of + /// the given data-flow facts holding right after Inst. + /// + /// The result is given as graph, where cycles are unrolled once in an + /// implementation-defined way. template < typename ConfigTy, typename L, typename Filter = DefaultPathTracingFilter, typename = std::enable_if_t>> @@ -159,6 +169,11 @@ class PathSensitivityManagerMixin { return pathsDagToAll(std::move(Inst), FactsRange, Config, PFilter); } + /// Reconstruct the combined control- and data-flow paths the lead to the + /// given data-flow fact Fact holding right after Inst. + /// + /// The result is given as graph, where cycles are unrolled once in an + /// implementation-defined way. template < typename ConfigTy, typename Filter = DefaultPathTracingFilter, typename = std::enable_if_t>> @@ -171,6 +186,11 @@ class PathSensitivityManagerMixin { PFilter); } + /// Reconstruct the combined control- and data-flow paths the lead to any of + /// the given data-flow facts in FactsRange holding at Inst. + /// + /// The result is given as graph, where cycles are unrolled once in an + /// implementation-defined way. template < typename ConfigTy, typename Filter = DefaultPathTracingFilter, typename = std::enable_if_t>> diff --git a/include/phasar/Domain/AnalysisDomain.h b/include/phasar/Domain/AnalysisDomain.h index bf79fe7970..a4622695a8 100644 --- a/include/phasar/Domain/AnalysisDomain.h +++ b/include/phasar/Domain/AnalysisDomain.h @@ -14,30 +14,31 @@ namespace psr { -// AnalysisDomain - This class should be specialized by different static -// analyses types... which is why the default version declares all analysis -// domains as aliases of void. -// -// Virtually all of PhASAR's internal analyses are implemented in a generic way -// using interfaces and template parameters. In order to specify concrete types -// for the template parameters such that an analysis can compute some useful -// information on some concrete target code, a configuration template parameter -// of type AnalysisDomain is passed around to make the necessary information -// available to the required analyses. -// -// If a type is not meant to be used by an analysis it should be left as an -// alias to void. If any analysis detects that a parameter is required to -// conduct an analysis but not correctly set, it will statically report an error -// and ask for the missing piece of information. +/// AnalysisDomain - This class should be specialized by different static +/// analyses types... which is why the default version declares all analysis +/// domains as aliases of void. +/// +/// Virtually all of PhASAR's internal analyses are implemented in a generic way +/// using interfaces and template parameters. In order to specify concrete types +/// for the template parameters such that an analysis can compute some useful +/// information on some concrete target code, a configuration template parameter +/// of type AnalysisDomain is passed around to make the necessary information +/// available to the required analyses. +/// +/// If a type is not meant to be used by an analysis it should be left as an +/// alias to void. If any analysis detects that a parameter is required to +/// conduct an analysis but not correctly set, it will statically report an +/// error and ask for the missing piece of information. struct AnalysisDomain { - // Data-flow fact --- Specifies the type of an individual data-flow fact that - // is propagated through the program under analysis. + /// Data-flow fact --- Specifies the type of an individual data-flow fact that + /// is propagated through the program under analysis. using d_t = void; // (Control-flow) Node --- Specifies the type of a node in the // (inter-procedural) control-flow graph and can be though of as an individual - // statement or instruction of the program. + // statement or instruction of the target program. using n_t = void; - // Function --- Specifies the type of functions. + // Function --- Specifies the type of functions/procedures in the target + // program. using f_t = void; // (User-defined) type --- Specifies the type of a user-defined (i.e. struct // or class) data type. diff --git a/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h index 348f341f41..9f0a7f8dc1 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h +++ b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h @@ -16,6 +16,12 @@ namespace psr { class LLVMProjectIRDB; +/// \brief Provides utilities to inject a function into the IR under analysis +/// that captures global constructors and destructors as described in the 2021 +/// Paper "Modeling the Effects of Global Variables in Data-Flow Analysis for +/// C/C++" by Schubert et al. +/// +/// See <10.1109/SCAM52516.2021.00010> for more information. class GlobalCtorsDtorsModel { public: static constexpr llvm::StringLiteral ModelName = @@ -30,14 +36,27 @@ class GlobalCtorsDtorsModel { static constexpr llvm::StringLiteral UserEntrySelectorName = "__psrCRuntimeUserEntrySelector"; + /// @brief Function that creates a new global model and inserts it into the + /// IRDB. The returned function is the global model that was inserted. + /// @param[in, out] IRDB IR database that will have the global model + /// inserted. + /// @param[in] UserEntryPoints Entry points for the program given as + /// llvm::Function pointers. You usually want to pass here the main function static llvm::Function * buildModel(LLVMProjectIRDB &IRDB, llvm::ArrayRef UserEntryPoints); + + /// @brief Function that creates a new global model and inserts it into the + /// IRDB. The returned function is the global model that was inserted. + /// @param[in, out] IRDB IR database that will have the global model + /// inserted. + /// @param[in] UserEntryPoints Entry points for the program given as + /// std::strings. You usually want to pass here the main function static llvm::Function * buildModel(LLVMProjectIRDB &IRDB, llvm::ArrayRef UserEntryPoints); - /// Returns true, if a function was generated by phasar. + /// Returns true, if a function was generated by the GlobalCtorsDtorsModel. [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &F) noexcept; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h index a488e9b469..be69af1348 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h @@ -22,6 +22,8 @@ namespace psr { class LLVMProjectIRDB; class LLVMBasedBackwardCFG; +/// \brief A class that represents a backwards control flow graph. Conforms to +/// the CFGBase CRTP interface. class LLVMBasedBackwardCFG : public detail::LLVMBasedCFGImpl { friend CFGBase; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h index d9bbb1786a..670a6a6c9d 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h @@ -25,6 +25,8 @@ template class CallGraph; template <> struct CFGTraits : CFGTraits {}; +/// \brief A class that represents a backwards interprocedural control flow +/// graph. Conforms to the ICFGBase CRTP interface. class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, public ICFGBase { friend ICFGBase; @@ -35,7 +37,7 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, public: LLVMBackwardRet(llvm::LLVMContext &Ctx) - : Instance(llvm::ReturnInst::Create(Ctx)){}; + : Instance(llvm::ReturnInst::Create(Ctx)) {}; [[nodiscard]] const llvm::ReturnInst *getInstance() const noexcept { return Instance; } diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h index 9eee1395d6..6a3f97c56e 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h @@ -36,6 +36,8 @@ template <> struct CFGTraits { template <> struct CFGTraits : CFGTraits {}; +/// \brief A class that implements a control flow graph. Conforms to the CFGBase +/// CRTP interface. namespace detail { template class LLVMBasedCFGImpl : public CFGBase { friend class LLVMBasedBackwardCFG; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h index 9ed1799e0b..fbb2693c95 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h @@ -44,6 +44,8 @@ class Resolver; class LLVMBasedICFG; template <> struct CFGTraits : CFGTraits {}; +/// \brief A class that implements a inter-procedural control flow graph. +/// Conforms to the ICFGBase CRTP interface. class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { friend ICFGBase; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h index 1fdc8100bf..646ff71322 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h @@ -23,6 +23,12 @@ class GlobalVariable; namespace psr { class LLVMProjectIRDB; +/// \brief A class that provides access to all C++ virtual function tables +/// (VTables) found in the target program. +/// +/// Useful for constructing a call graph for a C++-based target. +/// \note This class only works, if the target program's IR was generated with +/// debug information. Pass `-g` to the compiler to achieve this. class LLVMVFTableProvider { public: explicit LLVMVFTableProvider(const llvm::Module &Mod); diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h index 818a59de8d..dc6f7c8ff1 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h @@ -26,6 +26,9 @@ class CallBase; namespace psr { class DIBasedTypeHierarchy; + +/// \brief A resolver that performs Class Hierarchy Analysis to resolve calls +/// to C++ virtual functions. Requires debug information. class CHAResolver : public Resolver { public: CHAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h index 376eb59623..88afa796e5 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h @@ -18,6 +18,7 @@ class CallBase; namespace psr { +/// \brief A resolver that doesn't resolve indirect- and virtual calls class NOResolver final : public Resolver { public: NOResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP); diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h index 3bbdc83f5e..eca760ae77 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h @@ -36,6 +36,8 @@ namespace psr { class DIBasedTypeHierarchy; +/// \brief A resolver that uses alias information to resolve indirect and +/// virtual calls class OTFResolver : public Resolver { public: OTFResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h index f4371c6082..c6e003211f 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h @@ -28,6 +28,9 @@ class DICompositeType; namespace psr { class DIBasedTypeHierarchy; + +/// \brief A resolver that performs Rapid Type Analysis to resolve calls +/// to C++ virtual functions. Requires debug information. class RTAResolver : public CHAResolver { public: RTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h index 8748e56aa2..c59717c25f 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h @@ -68,6 +68,10 @@ getNonPureVirtualVFTEntry(const llvm::DIType *T, unsigned Idx, [[nodiscard]] bool isVirtualCall(const llvm::Instruction *Inst, const LLVMVFTableProvider &VTP); +/// \brief A base class for call-target resolvers. Used to build call graphs. +/// +/// Create a specific resolver by making a new class, inheriting this resolver +/// class and implementing the virtual functions as needed. class Resolver { protected: const LLVMProjectIRDB *IRDB; diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h index 8645f5c725..6524bfad04 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h @@ -23,6 +23,8 @@ template <> struct CFGTraits : CFGTraits { using v_t = const llvm::Value *; }; +/// \brief A class that implements a sparse control flow graph. Conforms to the +/// CFGBase CRTP interface. class SparseLLVMBasedCFG : public LLVMBasedCFG, public SparseCFGBase { friend struct SVFGCache; diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h index 2d43ae64ea..df946ae27a 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h @@ -20,12 +20,20 @@ class SparseLLVMBasedCFG; class DIBasedTypeHierarchy; struct SVFGCache; +/// \brief A class that implements a sparse interprocedural control flow graph. +/// Conforms to the ICFGBase CRTP interface. +/// +/// Use this in the IDESolver or IFDSSolver to profit from the SparseIFDS or +/// SparseIDE optimization after Karakays et al. "Symbol-Specific Sparsification +/// of Interprocedural Distributive Environment Problems" +/// class SparseLLVMBasedICFG : public LLVMBasedICFG, public SparseLLVMBasedCFGProvider { friend SparseLLVMBasedCFGProvider; public: + /// Constructor that delegates all arguments to the ctor of LLVMBasedICFG explicit SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, CallGraphAnalysisType CGType, llvm::ArrayRef EntryPoints = {}, diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h index 8c0aaa9dac..7fbbb65a5e 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h @@ -29,9 +29,14 @@ struct SVFGCache; template <> struct CFGTraits : CFGTraits {}; -/// Similar to SparseLLVMBasedICFG; the only difference is that this one *is* no -/// LLVMBasedICFG -- it contains a pointer to an already existing one. -/// It still owns the sparse value-flow graphs +/// \brief Similar to SparseLLVMBasedICFG; the only difference is that this one +/// *is* no LLVMBasedICFG -- it contains a pointer to an already existing one. +/// It still owns the sparse value-flow graphs. +/// +/// Use this in the IDESolver or IFDSSolver to profit from the SparseIFDS or +/// SparseIDE optimization after Karakays et al. "Symbol-Specific Sparsification +/// of Interprocedural Distributive Environment Problems" +/// class SparseLLVMBasedICFGView : public LLVMBasedCFG, public ICFGBase, diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index 677b26639d..0b970fcfed 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -36,6 +36,7 @@ template <> struct ProjectIRDBTraits { using g_t = const llvm::GlobalVariable *; }; +/// \brief Project IR Database that manages a LLVM IR module. class LLVMProjectIRDB : public ProjectIRDBBase { friend ProjectIRDBBase; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h index e40d109abe..98cdd56f73 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h @@ -23,9 +23,10 @@ struct DataFlowFact { std::variant Fact; }; +/// \brief Simple representation of a serializable data-flow summary class FunctionDataFlowFacts { public: - using ParamaterMappingTy = + using ParameterMappingTy = std::unordered_map>; FunctionDataFlowFacts() noexcept = default; @@ -67,10 +68,10 @@ class FunctionDataFlowFacts { return It->second; } - return getDefaultValue(); + return getDefaultValue(); } - llvm::StringMap Fdff; + llvm::StringMap Fdff; }; } // namespace psr::library_summary diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h index 19e03b49bf..8afba2f715 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h @@ -14,10 +14,11 @@ class LLVMFunctionDataFlowFacts; [[nodiscard]] LLVMFunctionDataFlowFacts readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); +/// @brief A LLVM-specific mapping of FunctionDataFlowFacts class LLVMFunctionDataFlowFacts { public: LLVMFunctionDataFlowFacts() noexcept = default; - using ParamaterMappingTy = FunctionDataFlowFacts::ParamaterMappingTy; + using ParameterMappingTy = FunctionDataFlowFacts::ParameterMappingTy; /// insert a set of data flow facts void insertSet(const llvm::Function *Fun, uint32_t Index, @@ -56,19 +57,19 @@ class LLVMFunctionDataFlowFacts { return getFacts(Fun, Arg->getArgNo()); } - [[nodiscard]] const ParamaterMappingTy & + [[nodiscard]] const ParameterMappingTy & getFactsForFunction(const llvm::Function *Fun) { auto Iter = LLVMFdff.find(Fun); if (Iter != LLVMFdff.end()) { return Iter->second; } - return getDefaultValue(); + return getDefaultValue(); } friend LLVMFunctionDataFlowFacts readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); private: - std::unordered_map LLVMFdff; + std::unordered_map LLVMFdff; }; } // namespace psr::library_summary diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h index ad55a98402..729e3c44a5 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h @@ -28,10 +28,9 @@ class Value; namespace psr { -/** - * This class may be used to represent the special zero value for IFDS - * and IDE problems. The LLVMZeroValue is implemented as a singleton. - */ +/// \brief This class may be used to represent the special zero value (aka. Λ) +/// for IFDS and IDE problems. The LLVMZeroValue is implemented as a singleton. +/// class LLVMZeroValue : public llvm::GlobalVariable { private: LLVMZeroValue(llvm::Module &Mod); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h index 34012ec334..bad969a70e 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h @@ -51,6 +51,8 @@ struct IDEExtendedTaintAnalysisDomain : public LLVMAnalysisDomainDefault { }; 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 { diff --git a/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h index 988a977c4a..b3c79d7032 100644 --- a/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h +++ b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h @@ -61,6 +61,12 @@ class Z3BasedPathSensitivityManagerBase static void deduplicatePaths(FlowPathSequence &Paths); }; +/// \brief An extension of the path-reconstruction mechanism of the +/// PathSensitivityManager that provides means to extract concrete combined +/// control- and data-flow paths. +/// +/// Filters out paths that are considered infeasible by the Z3 +/// constraint solver. template >> @@ -89,6 +95,14 @@ class Z3BasedPathSensitivityManager } } + /// Reconstruct the feasible control- and data-flow paths the lead to + /// the given data-flow fact Fact holding right after Inst. + /// + /// The result is given as list of paths, where cycles are unrolled once in an + /// implementation-defined way. + /// It is strongly recommended to Use the Z3BasedPathSensitivityConfig in the + /// Z3BasedPathSensitivityManager's ctor to limit the returned paths; + /// otherwise this function quickly becomes a performance bottleneck. FlowPathSequence pathsTo(n_t Inst, d_t Fact) const { if (Config.DAGSizeThreshold != SIZE_MAX) { PHASAR_LOG_LEVEL( diff --git a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h index 755bbc9230..c4330c4e4b 100644 --- a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h +++ b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h @@ -25,6 +25,8 @@ class LLVMProjectIRDB; class LLVMBasedICFG; class LLVMBasedCFG; +/// \brief An AnalysisDomain that specializes sensible defaults for LLVM-based +/// analysis struct LLVMAnalysisDomainDefault : public AnalysisDomain { using d_t = const llvm::Value *; using n_t = const llvm::Instruction *; @@ -36,6 +38,8 @@ struct LLVMAnalysisDomainDefault : public AnalysisDomain { using db_t = LLVMProjectIRDB; }; +/// \brief An AnalysisDomain that specializes sensible defaults for LLVM-based +/// IFDS analysis using LLVMIFDSAnalysisDomainDefault = WithBinaryValueDomain; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index 1c77b6f0cd..eeffe7412a 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -53,14 +53,15 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, using AllocationSiteSetPtrTy = traits_t::AllocationSiteSetPtrTy; using AliasSetMap = llvm::DenseMap>; - /** - * Creates points-to set(s) for all functions in the IRDB. If - * UseLazyEvaluation is true, computes points-to-sets for functions that do - * not use global variables on the fly - */ + /// \brief Creates alias-sets for all functions in the IRDB. + /// + /// If UseLazyEvaluation is true, computes alias-sets only for functions that + /// use global variables directly and delays all others to when they are first + /// requested explicit LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation = true, AliasAnalysisType PATy = AliasAnalysisType::CFLAnders); + /// Loads alias sets from JSON explicit LLVMAliasSet(LLVMProjectIRDB *IRDB, const nlohmann::json &SerializedPTS); @@ -103,22 +104,18 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, return AnalysisProperties::None; } - /** - * Shows a parts of an alias set. Good for debugging when one wants to peak - * into a points to set. - * - * @param ValueSetPair a pair on an Value* and the corresponding points to set - * @param Peak the amount of instrutions shown from the points to set - */ + /// Shows a parts of an alias set. Good for debugging when one wants to peak + /// into a points to set. + /// + /// \param ValueSetPair a pair on a Value* and the corresponding points-to set + /// \param Peak the amount of instructions shown from the points-to set static void peakIntoAliasSet(const AliasSetMap::value_type &ValueSetPair, int Peak); - /** - * Prints out the size distribution for all points to sets. - * - * @param Peak the amount of instrutions shown from one of the biggest points - * to sets, use 0 show nothing. - */ + /// Prints out the size distribution for all points to sets. + /// + /// \param Peak the amount of instructions shown from one of the biggest + /// points-to sets, use 0 to show nothing. void drawAliasSetsDistribution(int Peak = 10) const; [[nodiscard]] inline bool empty() const { return AnalyzedFunctions.empty(); } diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h index bf924e39cf..d895741ad2 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h @@ -15,6 +15,9 @@ #include namespace psr { + +/// A data structure used for storing, serializing and deserializing a +/// LLVMAliasSet struct LLVMAliasSetData { std::vector> AliasSets; std::vector AnalyzedFunctions; diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h index 1b5694c6d4..d4554958a8 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h @@ -25,6 +25,10 @@ namespace psr { class LLVMProjectIRDB; +/// \brief Represents the type hierarchy of the target program. +/// +/// \note This class only works, if the target program's IR was generated with +/// debug information. Pass `-g` to the compiler to achieve this. class DIBasedTypeHierarchy : public TypeHierarchy { public: @@ -39,7 +43,17 @@ class DIBasedTypeHierarchy static inline constexpr llvm::StringLiteral PureVirtualCallName = "__cxa_pure_virtual"; + /// \brief Creates a type hierarchy based on an intermediate representation + /// database. + /// \param[in] IRDB The IR database of which the type hierarchy will be based + /// upon. This MUST contain debug information for the algorithm to work! explicit DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB); + + /// \brief Loads an already computed type hierarchy. + /// \param[in] IRDB The IR database of the type hierarchy. + /// \param[in] SerializedData The already existing type hierarchy, given by + /// the appropiate class DIBasedTypeHierarchyData, which contains all + /// neccesary information. explicit DIBasedTypeHierarchy(const LLVMProjectIRDB *IRDB, const DIBasedTypeHierarchyData &SerializedData); ~DIBasedTypeHierarchy() override = default; @@ -93,16 +107,12 @@ class DIBasedTypeHierarchy void print(llvm::raw_ostream &OS = llvm::outs()) const override; - /** - * @brief Prints the class hierarchy to an ostream in dot format. - * @param OS outputstream - */ + /// \brief Prints the class hierarchy to an ostream in dot format. + /// \param OS outputstream void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; - /** - * @brief Prints the class hierarchy to an ostream in json format. - * @param an outputstream - */ + /// \brief Prints the class hierarchy to an ostream in JSON format. + /// \param OS outputstream void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const override; private: diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h index 52cd855699..da579b8f83 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h @@ -18,6 +18,9 @@ #include namespace psr { +/// \brief A structure that is used to store already calculated type hierarchy +/// data, serialize that data or deserialize a json file with a previously +/// serialized type hierarchy. struct DIBasedTypeHierarchyData { // DITypes and llvm::Function * are serialized by serializing their names and // using the DebugInfoFinder to deserialize them diff --git a/include/phasar/PhasarLLVM/Utils/Annotation.h b/include/phasar/PhasarLLVM/Utils/Annotation.h index 31d9c42ba0..bf5527e7cc 100644 --- a/include/phasar/PhasarLLVM/Utils/Annotation.h +++ b/include/phasar/PhasarLLVM/Utils/Annotation.h @@ -10,9 +10,8 @@ namespace psr { -//===----------------------------------------------------------------------===// -// Helper classes that allow for an easier retrieval of annotation information. -//===----------------------------------------------------------------------===// +/// \file Helper classes that allow for an easier retrieval of annotation +/// information. class VarAnnotation { public: diff --git a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h index f9d3d91d72..fd57c81e0d 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h @@ -39,6 +39,11 @@ class DILocation; namespace psr { +/// \file This file contains useful structs and functions to get and store +/// information about the source code or the intermediate representation of the +/// target being analyzed. + +/// \brief Minimal source-code information, based on LLVM debug information struct DebugLocation { unsigned Line{}; unsigned Column{}; @@ -47,6 +52,8 @@ struct DebugLocation { [[nodiscard]] llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V); +/// \brief A struct that contains information about a source code line, function +/// name, file name corresponding to the IR statement. struct SourceCodeInfo { std::string SourceCodeLine; std::string SourceCodeFilename; diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index b0d8bccf9a..1292499460 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -53,11 +53,23 @@ bool isIntegerLikeType(const llvm::Type *T) noexcept; bool isAllocaInstOrHeapAllocaFunction(const llvm::Value *V) noexcept; bool isHeapAllocatingFunction(const llvm::Function *F) noexcept; -// TODO add description +/// Returns true if the provided function and the function type are both not +/// null and have the same number of parameters and the same return type. If the +/// argument ExactMatch is set to true, which it is by default, the two provided +/// arguments must also have the same type for each argument, for the function +/// to return true. +/// +/// \note This function is less useful in practice than you may think. Consider +/// using isConsistentCall() instead. bool matchesSignature(const llvm::Function *F, const llvm::FunctionType *FType, bool ExactMatch = true); -// TODO add description +/// Returns true iff the provided functions are both not null and have the same +/// number of paramters, the same return type and each parameter of both +/// functions has the same type aswell. +/// +/// \note This function is less useful in practice than you may think. Consider +/// using isConsistentCall() instead. bool matchesSignature(const llvm::FunctionType *FType1, const llvm::FunctionType *FType2); @@ -230,7 +242,7 @@ bool isGuardVariable(const llvm::Value *V); bool isStaticVariableLazyInitializationBranch(const llvm::BranchInst *Inst); /** - * Tests for https://llvm.org/docs/LangRef.html#llvm-var-annotation-intrinsic + * Tests for * e.g. * int boo __attribute__((annotate("bar")); * @param F The function to test - Target of the call instruction @@ -239,7 +251,7 @@ bool isVarAnnotationIntrinsic(const llvm::Function *F); /** * Retrieves String annotation value as per - * https://llvm.org/docs/LangRef.html#llvm-var-annotation-intrinsic + * * Test the call function be tested by isVarAnnotationIntrinsic * */ diff --git a/include/phasar/PhasarPass/PhasarPass.h b/include/phasar/PhasarPass/PhasarPass.h index d8272b98c3..53fdc174a5 100644 --- a/include/phasar/PhasarPass/PhasarPass.h +++ b/include/phasar/PhasarPass/PhasarPass.h @@ -20,6 +20,20 @@ class raw_ostream; namespace psr { +/// \brief PhasarPass is an implementation of llvm passes for the PhASAR +/// framework. +/// +/// Allows to run phasar-based analyses with LLVM's pass manager for easy +/// integration into opt/clang. +/// +/// What is a pass? +/// "The LLVM pass framework is an important part of the LLVM system, +/// because LLVM passes are where most of the interesting parts of the compiler +/// exist. Passes perform the transformations and optimizations that make up the +/// compiler, they build the analysis results that are used by these +/// transformations, and they are, above all, a structuring technique for +/// compiler code." +/// Source: class PhasarPass : public llvm::ModulePass { public: static inline char ID = 12; diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index db861acdca..a5f2f5f577 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -41,7 +41,7 @@ struct AliasInfoTraits> : DefaultAATraits {}; template struct AliasInfoTraits> : DefaultAATraits {}; -/// A type-erased reference to any object implementing the IsAliasInfo +/// \brief A type-erased reference to any object implementing the IsAliasInfo /// interface. Use this, if your analysis is not tied to a specific alias info /// implementation. /// @@ -260,8 +260,9 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { const VTable *VT{}; }; -/// Similar to AliasInfoRef, but exclusively owns the held reference. Use this, -/// if you need to decide dynamically, which alias info implementation to use. +/// \brief Similar to AliasInfoRef, but exclusively owns the held reference. Use +/// this, if you need to decide dynamically, which alias info implementation to +/// use. /// /// Implicitly convertible to AliasInfoRef. /// @@ -314,13 +315,13 @@ class [[clang::trivial_abi]] AliasInfo final : public AliasInfoRef { } } - [[nodiscard]] base_t asRef() &noexcept { return *this; } - [[nodiscard]] AliasInfoRef asRef() const &noexcept { return *this; } + [[nodiscard]] base_t asRef() & noexcept { return *this; } + [[nodiscard]] AliasInfoRef asRef() const & noexcept { return *this; } [[nodiscard]] AliasInfoRef asRef() && = delete; /// For better interoperability with unique_ptr - [[nodiscard]] base_t get() &noexcept { return asRef(); } - [[nodiscard]] AliasInfoRef get() const &noexcept { return asRef(); } + [[nodiscard]] base_t get() & noexcept { return asRef(); } + [[nodiscard]] AliasInfoRef get() const & noexcept { return asRef(); } [[nodiscard]] AliasInfoRef get() && = delete; }; diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index a236f2e345..4fc331cf53 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -20,6 +20,7 @@ class raw_ostream; namespace psr { +/// \brief A generic class to represent a virtual function table template class VFTable { public: virtual ~VFTable() = default; diff --git a/include/phasar/Utils/AnalysisPrinterBase.h b/include/phasar/Utils/AnalysisPrinterBase.h index a1ca6aee39..ec95b47d79 100644 --- a/include/phasar/Utils/AnalysisPrinterBase.h +++ b/include/phasar/Utils/AnalysisPrinterBase.h @@ -10,6 +10,8 @@ namespace psr { +/// \brief A generic class that serves as the basis for a custom analysis +/// printer implementation. template class AnalysisPrinterBase { using n_t = typename AnalysisDomainTy::n_t; using d_t = typename AnalysisDomainTy::d_t; diff --git a/include/phasar/Utils/DebugOutput.h b/include/phasar/Utils/DebugOutput.h index 03f6098b38..bfd282183f 100644 --- a/include/phasar/Utils/DebugOutput.h +++ b/include/phasar/Utils/DebugOutput.h @@ -23,6 +23,9 @@ namespace psr { namespace detail { +/// \file This file contains many useful ways of printing information for +/// debugging purposes. + template void printHelper(OS_t &OS, const T &Data); template diff --git a/include/phasar/Utils/DefaultAnalysisPrinter.h b/include/phasar/Utils/DefaultAnalysisPrinter.h index f9008b9461..8923c21d58 100644 --- a/include/phasar/Utils/DefaultAnalysisPrinter.h +++ b/include/phasar/Utils/DefaultAnalysisPrinter.h @@ -30,6 +30,8 @@ template struct Warning { LatticeElement(std::move(Lattice)), AnalysisType(DfAnalysisType) {} }; +/// \brief A default implementation of the AnalysisPrinterBase. Aggregates all +/// analysis results in a vector and prints them when the analysis is finished. template class DefaultAnalysisPrinter : public AnalysisPrinterBase { using n_t = typename AnalysisDomainTy::n_t; @@ -41,7 +43,8 @@ class DefaultAnalysisPrinter : public AnalysisPrinterBase { : OS(&OS) {} explicit DefaultAnalysisPrinter(const llvm::Twine &Filename) - : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + : AnalysisPrinterBase(), + OS(openFileStream(Filename)) {}; ~DefaultAnalysisPrinter() override = default; diff --git a/include/phasar/Utils/EquivalenceClassMap.h b/include/phasar/Utils/EquivalenceClassMap.h index a11d3fe4ed..20691aa3cf 100644 --- a/include/phasar/Utils/EquivalenceClassMap.h +++ b/include/phasar/Utils/EquivalenceClassMap.h @@ -23,11 +23,12 @@ namespace psr { -// EquivalenceClassMap is a special map type that splits the keys into -// equivalence classes regarding their mapped values. Meaning, that all keys -// that are equivalent are mapped to the same value. Two keys are treated as -// equivalent and merged into a equivalence class when they refer to Values -// that compare equal. +/// \brief EquivalenceClassMap is a special map type that splits the keys into +/// equivalence classes regarding their mapped values. +/// +/// Meaning, that all keys that are equivalent are mapped to the same value. Two +/// keys are treated as equivalent and merged into an equivalence class when +/// they refer to Values that are considered equal according to operator==. template struct EquivalenceClassMap { template using SetType = std::set; using EquivalenceClassBucketT = std::pair, ValueT>; @@ -69,41 +70,41 @@ template struct EquivalenceClassMap { return llvm::make_range(begin(), end()); } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. template insert_return_type insert(const KeyT &Key, ValueType &&Value) { return try_emplace(Key, std::forward(Value)); } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. template insert_return_type insert(KeyT &&Key, ValueType &&Value) { return try_emplace(std::move(Key), std::forward(Value)); } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. insert_return_type insert(const std::pair &KVPair) { return try_emplace(KVPair.first, KVPair.second); } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. insert_return_type insert(std::pair &&KVPair) { return try_emplace(std::move(KVPair.first), std::move(KVPair.second)); } - // Insert a range of Key Values pairs into the map. + /// Insert a range of Key Values pairs into the map. template void insert(InputIt I, InputIt End) { for (; I != End; ++I) { try_emplace(I->first, I->second); } } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. template insert_return_type try_emplace(KeyT &&Key, Ts &&...Args) { ValueT Val{std::forward(Args...)}; @@ -118,8 +119,8 @@ template struct EquivalenceClassMap { return std::make_pair(StoredData.back().first.begin(), true); } - // Inserts Key into the corresponding equivalence class for Value. If Value - // is not already in the map a new equivalence class is created. + /// Inserts Key into the corresponding equivalence class for Value. If Value + /// is not already in the map a new equivalence class is created. template insert_return_type try_emplace(const KeyT &Key, Ts &&...Args) { ValueT Val{std::forward(Args...)}; @@ -134,7 +135,7 @@ template struct EquivalenceClassMap { return std::make_pair(StoredData.back().first.begin(), true); } - // Return 1 if the specified key is in the map, 0 otherwise. + /// Return 1 if the specified key is in the map, 0 otherwise. [[nodiscard]] inline size_type count(const KeyT &Key) const { for (auto &KVPair : StoredData) { if (KVPair.first.count(Key) >= 1) { @@ -148,7 +149,7 @@ template struct EquivalenceClassMap { return StoredData.size(); } - // Returns the size of the map, i.e., the number of equivalence classes. + /// Returns the size of the map, i.e., the number of equivalence classes. [[nodiscard]] inline size_type size() const { return numEquivalenceClasses(); } @@ -273,7 +274,7 @@ class EquivalenceClassMapNG { return Values.size(); } - // Returns the size of the map, i.e., the number of equivalence classes. + /// Returns the size of the map, i.e., the number of equivalence classes. [[nodiscard]] inline size_t size() const noexcept { return numEquivalenceClasses(); } diff --git a/include/phasar/Utils/ErrorHandling.h b/include/phasar/Utils/ErrorHandling.h index 3e5920af13..93dfacf2c4 100644 --- a/include/phasar/Utils/ErrorHandling.h +++ b/include/phasar/Utils/ErrorHandling.h @@ -18,6 +18,10 @@ #include namespace psr { + +/// \file This file contains useful functions for handling errors, by using +/// std::system_error, or returning null or a default value. + template T getOrThrow(llvm::ErrorOr ValOrErr) { if (ValOrErr) { return std::move(*ValOrErr); diff --git a/include/phasar/Utils/IO.h b/include/phasar/Utils/IO.h index f9966df631..27669460b3 100644 --- a/include/phasar/Utils/IO.h +++ b/include/phasar/Utils/IO.h @@ -28,6 +28,9 @@ namespace psr { +/// \file This file contains functions for reading in text files and json files +/// and provides error handling capabilities as well, if needed. + [[nodiscard]] llvm::ErrorOr readTextFileOrErr(const llvm::Twine &Path); [[nodiscard]] llvm::ErrorOr> diff --git a/include/phasar/Utils/OnTheFlyAnalysisPrinter.h b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h index aa3e4ef232..9d3bf73270 100644 --- a/include/phasar/Utils/OnTheFlyAnalysisPrinter.h +++ b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h @@ -13,6 +13,10 @@ #include namespace psr { +/// This class implements the AnalysisPrinterBase that prints the analysis +/// results *while* the analysis is still running. +/// +/// Override doOnResult() to customize, how the results are printed. template class OnTheFlyAnalysisPrinter : public AnalysisPrinterBase { using n_t = typename AnalysisDomainTy::n_t; @@ -21,10 +25,11 @@ class OnTheFlyAnalysisPrinter : public AnalysisPrinterBase { public: explicit OnTheFlyAnalysisPrinter(llvm::raw_ostream &OS) - : AnalysisPrinterBase(), OS(&OS){}; + : AnalysisPrinterBase(), OS(&OS) {}; explicit OnTheFlyAnalysisPrinter(const llvm::Twine &Filename) - : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + : AnalysisPrinterBase(), + OS(openFileStream(Filename)) {}; OnTheFlyAnalysisPrinter() = default; ~OnTheFlyAnalysisPrinter() = default; diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index f2c7504c74..612732679f 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -147,7 +147,7 @@ struct StringIDLess { bool operator()(const std::string &LHS, const std::string &RHS) const; }; -/// See "https://en.cppreference.com/w/cpp/experimental/scope_exit/scope_exit" +/// See template class scope_exit { // NOLINT public: template ()())> @@ -168,14 +168,16 @@ template class scope_exit { // NOLINT template scope_exit(Fn) -> scope_exit; -// Copied from "https://en.cppreference.com/w/cpp/utility/variant/visit" -template struct Overloaded : Ts... { using Ts::operator()...; }; +// Copied from +template struct Overloaded : Ts... { + using Ts::operator()...; +}; // explicit deduction guide (not needed as of C++20) template Overloaded(Ts...) -> Overloaded; /// Based on the reference implementation of std::remove_if -/// "https://en.cppreference.com/w/cpp/algorithm/remove" and optimized for the +/// and optimized for the /// case that a sorted list of indices is given instead of an unary predicate /// specifying the elements to be removed. template template [[nodiscard]] constexpr auto &&forward_like(U &&X) noexcept { // NOLINT // NOLINTNEXTLINE diff --git a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h index 2195d02ff4..4f47e7428d 100644 --- a/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h +++ b/lib/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h @@ -29,6 +29,10 @@ namespace psr { class LLVMProjectIRDB; +/// \brief Wrapper over alias analyses that provide point-wise alias +/// information. +/// +/// Used to construct an LLVMAliasSet. class LLVMBasedAliasAnalysis : public AliasAnalysisView { public: explicit LLVMBasedAliasAnalysis( diff --git a/lib/PhasarLLVM/TaintConfig/LLVMTaintConfig.cpp b/lib/PhasarLLVM/TaintConfig/LLVMTaintConfig.cpp index c7465cdd85..fbdb822424 100644 --- a/lib/PhasarLLVM/TaintConfig/LLVMTaintConfig.cpp +++ b/lib/PhasarLLVM/TaintConfig/LLVMTaintConfig.cpp @@ -366,7 +366,7 @@ void LLVMTaintConfig::forAllLeakCandidatesAtImpl( } } - // Do not iterate over the actual paramaters of Inst as we did in + // Do not iterate over the actual parameters of Inst as we did in // forAllGeneratedValuesAt, because sink-values are not propagated in the // current taint analyses. Handling sink-values should be done in the // SinkCallBack From 64e824199f40192e32b8fb2f93aec64f5ebffd5a Mon Sep 17 00:00:00 2001 From: Maximilian Leo Huber Date: Wed, 18 Jun 2025 12:21:01 +0000 Subject: [PATCH 10/24] Added default reachable allocation sites functions (#781) * Base files * Added missing alias elements * tests + normalflow impl * added RAS tests * WIP reworking flow functions * ret and call flow, tests seq fault * fixed seq faults in tests * fixed bugs and unittests * Cleanup + small fixes * pre-commit * Add missing header guards and file headers --------- Co-authored-by: Fabian Schiebel --- .../IfdsIde/DefaultAliasAwareIDEProblem.h | 41 ++-- .../IfdsIde/DefaultNoAliasIDEProblem.h | 24 +- ...efaultReachableAllocationSitesIDEProblem.h | 150 ++++++++++++ .../DataFlow/IfdsIde/FunctionDataFlowFacts.h | 13 ++ .../IfdsIde/LLVMFunctionDataFlowFacts.h | 14 ++ .../PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h | 14 +- .../DefaultAliasAwareIDEFlowFunctions.cpp | 14 -- ...aultReachableAllocationSitesIDEProblem.cpp | 123 ++++++++++ .../IfdsIde/DefaultFlowFunctionTest.cpp | 217 +++++++++++++++++- 9 files changed, 551 insertions(+), 59 deletions(-) create mode 100644 include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h create mode 100644 lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h index 9a73977f97..98933d2caf 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -1,3 +1,12 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, mxHuber and others + *****************************************************************************/ + #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDEALIASINFOTABULATIONPROBLEM_H @@ -38,17 +47,14 @@ class IDEAliasAwareDefaultFlowFunctionsImpl [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, n_t /*Succ*/); - [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, - f_t CalleeFun); [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, n_t /*RetSite*/); - [[nodiscard]] FlowFunctionPtrType - getCallToRetFlowFunctionImpl(n_t CallSite, n_t /*RetSite*/, - llvm::ArrayRef /*Callees*/); + using IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl; + using IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl; -private: +protected: LLVMAliasInfoRef AS; }; } // namespace detail @@ -58,22 +64,7 @@ class DefaultAliasAwareIDEProblem : public IDETabulationProblem, protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { public: - using ProblemAnalysisDomain = AnalysisDomainTy; - using d_t = typename AnalysisDomainTy::d_t; - using n_t = typename AnalysisDomainTy::n_t; - using f_t = typename AnalysisDomainTy::f_t; - using t_t = typename AnalysisDomainTy::t_t; - using v_t = typename AnalysisDomainTy::v_t; - using l_t = typename AnalysisDomainTy::l_t; - 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; - - using container_type = typename FlowFunctionType::container_type; + using typename IDETabulationProblem::db_t; /// Constructs an IDETabulationProblem with the usual arguments + alias /// information. @@ -86,9 +77,11 @@ class DefaultAliasAwareIDEProblem std::optional ZeroValue) noexcept(std::is_nothrow_move_constructible_v) : IDETabulationProblem(IRDB, std::move(EntryPoints), - std::move(ZeroValue)), + ZeroValue), detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} + using detail::IDEAliasAwareDefaultFlowFunctionsImpl::getAliasInfo; + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { return getNormalFlowFunctionImpl(Curr, Succ); @@ -129,6 +122,8 @@ class DefaultAliasAwareIFDSProblem : IFDSTabulationProblem(IRDB, std::move(EntryPoints), ZeroValue), detail::IDEAliasAwareDefaultFlowFunctionsImpl(AS) {} + using detail::IDEAliasAwareDefaultFlowFunctionsImpl::getAliasInfo; + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { return getNormalFlowFunctionImpl(Curr, Succ); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h index 1865f749f2..3bb35b42ca 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h @@ -1,3 +1,12 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, mxHuber and others + *****************************************************************************/ + #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IDENOALIASINFOTABULATIONPROBLEM_H @@ -48,21 +57,6 @@ class DefaultNoAliasIDEProblem : public IDETabulationProblem, protected detail::IDENoAliasDefaultFlowFunctionsImpl { public: - using ProblemAnalysisDomain = AnalysisDomainTy; - using d_t = typename AnalysisDomainTy::d_t; - using n_t = typename AnalysisDomainTy::n_t; - using f_t = typename AnalysisDomainTy::f_t; - using t_t = typename AnalysisDomainTy::t_t; - using v_t = typename AnalysisDomainTy::v_t; - using l_t = typename AnalysisDomainTy::l_t; - 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; - using IDETabulationProblem::IDETabulationProblem; [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h new file mode 100644 index 0000000000..eac661ccd4 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h @@ -0,0 +1,150 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, mxHuber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_DEFAULTREACHABLEALLOCATIONSITESIDEPROBLEM_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_DEFAULTREACHABLEALLOCATIONSITESIDEPROBLEM_H + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +// Forward declaration of types for which we only use its pointer or ref type +namespace llvm { +class Value; +class Instruction; +class Function; +} // namespace llvm + +namespace psr { + +namespace detail { +class IDEReachableAllocationSitesDefaultFlowFunctionsImpl + : private IDENoAliasDefaultFlowFunctionsImpl { +public: + using typename IDENoAliasDefaultFlowFunctionsImpl::d_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::f_t; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionPtrType; + using typename IDENoAliasDefaultFlowFunctionsImpl::FlowFunctionType; + using typename IDENoAliasDefaultFlowFunctionsImpl::n_t; + + using IDENoAliasDefaultFlowFunctionsImpl::isFunctionModeled; + + [[nodiscard]] constexpr LLVMAliasInfoRef getAliasInfo() const noexcept { + return AS; + } + + constexpr IDEReachableAllocationSitesDefaultFlowFunctionsImpl( + LLVMAliasInfoRef AS) noexcept + : AS(AS) { + assert(AS && "You must provide an alias information handle!"); + } + + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunctionImpl(n_t Curr, + n_t /*Succ*/); + [[nodiscard]] FlowFunctionPtrType getCallFlowFunctionImpl(n_t CallInst, + f_t CalleeFun); + [[nodiscard]] FlowFunctionPtrType getRetFlowFunctionImpl(n_t CallSite, + f_t /*CalleeFun*/, + n_t ExitInst, + n_t /*RetSite*/); + + using IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl; + +protected: + LLVMAliasInfoRef AS; +}; +} // namespace detail + +template +class DefaultReachableAllocationSitesIDEProblem + : public IDETabulationProblem, + protected detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl { +public: + using typename IDETabulationProblem::db_t; + + /// Constructs an IDETabulationProblem 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 DefaultReachableAllocationSitesIDEProblem( + const ProjectIRDBBase *IRDB, LLVMAliasInfoRef 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: + /// 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 ProjectIRDBBase *IRDB, LLVMAliasInfoRef AS, + std::vector EntryPoints, + d_t ZeroValue) noexcept(std::is_nothrow_move_constructible_v) + : IFDSTabulationProblem(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); + } +}; + +} // namespace psr + +#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h index 98cdd56f73..ccb4c6b7c2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h @@ -1,3 +1,14 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, bulletspace and others + *****************************************************************************/ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FUNCTIONDATAFLOWFACTS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FUNCTIONDATAFLOWFACTS_H + #include "phasar/Utils/DefaultValue.h" #include "llvm/ADT/StringMap.h" @@ -75,3 +86,5 @@ class FunctionDataFlowFacts { }; } // namespace psr::library_summary + +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_FUNCTIONDATAFLOWFACTS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h index 8afba2f715..da04b93b1b 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h @@ -1,3 +1,15 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, bulletspace and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H + #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h" #include "phasar/Utils/DefaultValue.h" @@ -73,3 +85,5 @@ class LLVMFunctionDataFlowFacts { std::unordered_map LLVMFdff; }; } // namespace psr::library_summary + +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFUNCTIONDATAFLOWFACTS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h index cea1c2772c..fdb0c7a164 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h @@ -1,4 +1,14 @@ -#pragma once +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel, bulletspace and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LIBCSUMMARY_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LIBCSUMMARY_H namespace psr { namespace library_summary { @@ -7,3 +17,5 @@ class FunctionDataFlowFacts; [[nodiscard]] const library_summary::FunctionDataFlowFacts &getLibCSummary(); } // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LIBCSUMMARY_H diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp index ed18352cee..82725b65b1 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEFlowFunctions.cpp @@ -39,12 +39,6 @@ auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( Curr, Succ); } -auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( - n_t CallInst, f_t CalleeFun) -> FlowFunctionPtrType { - return this->IDENoAliasDefaultFlowFunctionsImpl::getCallFlowFunctionImpl( - CallInst, CalleeFun); -} - static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, const llvm::Instruction *Context) { container_type Tmp = Facts; @@ -92,11 +86,3 @@ auto detail::IDEAliasAwareDefaultFlowFunctionsImpl::getRetFlowFunctionImpl( return FFTemplates::killAllFlows(); } - -auto detail::IDEAliasAwareDefaultFlowFunctionsImpl:: - getCallToRetFlowFunctionImpl(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) - -> FlowFunctionPtrType { - return this->IDENoAliasDefaultFlowFunctionsImpl::getCallToRetFlowFunctionImpl( - CallSite, RetSite, Callees); -} diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp new file mode 100644 index 0000000000..f76f660ca8 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.cpp @@ -0,0 +1,123 @@ +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h" + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" + +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Casting.h" + +#include + +using namespace psr; + +using FFTemplates = FlowFunctionTemplates< + detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl::d_t, + detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: + FlowFunctionType::container_type>; +using container_type = FFTemplates::container_type; + +auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: + getNormalFlowFunctionImpl(n_t Curr, n_t Succ) -> FlowFunctionPtrType { + + if (const auto *Store = llvm::dyn_cast(Curr)) { + + auto AliasSet = + AS.getReachableAllocationSites(Store->getPointerOperand(), true, Store); + + container_type Gen(AliasSet->begin(), AliasSet->end()); + + return FFTemplates::lambdaFlow( + [Store, Gen{std::move(Gen)}, AS = AS](d_t Source) -> container_type { + if (Store->getPointerOperand() == Source) { + return {}; + } + if (Store->getValueOperand() == Source || + AS.isInReachableAllocationSites(Store->getValueOperand(), Source, + true, Store)) { + auto Ret = Gen; + Ret.insert(Source); + return Ret; + } + + return {Source}; + }); + } + + if (const auto *Load = llvm::dyn_cast(Curr)) { + return FFTemplates::lambdaFlow( + [Load, AS = AS](d_t Source) -> container_type { + if (Load->getPointerOperand() == Source || + AS.isInReachableAllocationSites(Load->getPointerOperand(), Source, + true, Load)) { + return {Source, Load}; + } + + return {Source}; + }); + } + + return this->IDENoAliasDefaultFlowFunctionsImpl::getNormalFlowFunctionImpl( + Curr, Succ); +} + +auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: + getCallFlowFunctionImpl(n_t CallInst, f_t CalleeFun) + -> FlowFunctionPtrType { + const auto *Call = llvm::cast(CallInst); + + return mapFactsToCallee( + Call, CalleeFun, [Call, AS = AS](d_t Arg, d_t Source) -> bool { + if (Arg == Source) { + return true; + } + + return Arg->getType()->isPointerTy() && + Source->getType()->isPointerTy() && + AS.isInReachableAllocationSites(Arg, Source, true, Call); + }); +} + +static void populateWithMayAliases(LLVMAliasInfoRef AS, container_type &Facts, + const llvm::Instruction *Context) { + container_type Tmp = Facts; + for (const auto *Fact : Tmp) { + auto Aliases = AS.getReachableAllocationSites(Fact, true, Context); + Facts.insert(Aliases->begin(), Aliases->end()); + } +} + +auto detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl:: + getRetFlowFunctionImpl(n_t CallSite, f_t /*CalleeFun*/, n_t ExitInst, + n_t /*RetSite*/) -> FlowFunctionPtrType { + const auto *Call = llvm::cast(CallSite); + const auto PostProcessFacts = [AS = AS, Call](container_type &Facts) { + populateWithMayAliases(AS, Facts, Call); + }; + + return mapFactsToCaller( + Call, ExitInst, + [AS = AS, ExitInst](d_t Param, d_t Source) { + if (!Param->getType()->isPointerTy()) { + return false; + } + + if (Param == Source) { + return true; + } + + // Arguments are counted as allocation-sites, so we have generated them + // as aliases + return !llvm::isa(Source) && + AS.isInReachableAllocationSites(Param, Source, true, ExitInst); + }, + [AS = AS, ExitInst](d_t Ret, d_t Source) { + if (Ret == Source) { + return true; + } + + return Ret->getType()->isPointerTy() && + Source->getType()->isPointerTy() && + AS.isInReachableAllocationSites(Ret, Source, true, ExitInst); + }, + {}, true, true, PostProcessFacts); +} diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp index 71d12e3129..619617ccd0 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp @@ -1,6 +1,7 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h" #include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" @@ -24,7 +25,7 @@ namespace { class IDEAliasImpl : public DefaultAliasAwareIFDSProblem { public: IDEAliasImpl(LLVMProjectIRDB *IRDB) - : DefaultAliasAwareIFDSProblem(IRDB, &PT, {}, {}), PT(IRDB){}; + : DefaultAliasAwareIFDSProblem(IRDB, &PT, {}, {}), PT(IRDB) {}; [[nodiscard]] InitialSeeds initialSeeds() override { return {}; @@ -37,13 +38,28 @@ class IDEAliasImpl : public DefaultAliasAwareIFDSProblem { class IDENoAliasImpl : public DefaultNoAliasIFDSProblem { public: IDENoAliasImpl(LLVMProjectIRDB *IRDB) - : DefaultNoAliasIFDSProblem(IRDB, {}, {}){}; + : DefaultNoAliasIFDSProblem(IRDB, {}, {}) {}; [[nodiscard]] InitialSeeds initialSeeds() override { return {}; }; }; +class IDEReachableAllocationSitesImpl + : public DefaultReachableAllocationSitesIFDSProblem { +public: + IDEReachableAllocationSitesImpl(LLVMProjectIRDB *IRDB) + : DefaultReachableAllocationSitesIFDSProblem(IRDB, &PT, {}, {}), + PT(IRDB) {}; + + [[nodiscard]] InitialSeeds initialSeeds() override { + return {}; + }; + +private: + FilteredLLVMAliasSet PT; +}; + std::set getNormalFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, const llvm::Value *Arg) { @@ -62,6 +78,15 @@ getNormalFlowValueSet(const llvm::Instruction *Instr, return NoAliasLLVMValueSet; } +std::set +getNormalFlowValueSet(const llvm::Instruction *Instr, + IDEReachableAllocationSitesImpl &RASImpl, + const llvm::Value *Arg) { + const auto RASNormalFlowFunc = RASImpl.getNormalFlowFunction(Instr, nullptr); + const auto RASLLVMValueSet = RASNormalFlowFunc->computeTargets(Arg); + return RASLLVMValueSet; +} + std::set getCallFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, const llvm::Value *Arg, const llvm::Function *CalleeFunc) { @@ -82,6 +107,16 @@ getCallFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, return NoAliasLLVMValueSet; } +std::set +getCallFlowValueSet(const llvm::Instruction *Instr, + IDEReachableAllocationSitesImpl &RASImpl, + const llvm::Value *Arg, const llvm::Function *CalleeFunc) { + const auto RASCallFlowFunc = RASImpl.getCallFlowFunction(Instr, CalleeFunc); + std::set RASLLVMValueSet = + RASCallFlowFunc->computeTargets(Arg); + return RASLLVMValueSet; +} + std::set getRetFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, const llvm::Value *Arg, const llvm::Instruction *ExitInst) { @@ -102,6 +137,17 @@ getRetFlowValueSet(const llvm::Instruction *Instr, IDENoAliasImpl &NoAliasImpl, return NoAliasLLVMValueSet; } +std::set +getRetFlowValueSet(const llvm::Instruction *Instr, + IDEReachableAllocationSitesImpl &RASImpl, + const llvm::Value *Arg, const llvm::Instruction *ExitInst) { + const auto RASRetFlowFunc = + RASImpl.getRetFlowFunction(Instr, nullptr, ExitInst, nullptr); + std::set RASLLVMValueSet = + RASRetFlowFunc->computeTargets(Arg); + return RASLLVMValueSet; +} + std::set getCallToRetFlowValueSet(const llvm::Instruction *Instr, IDEAliasImpl &AliasImpl, const llvm::Value *Arg) { @@ -121,6 +167,16 @@ getCallToRetFlowValueSet(const llvm::Instruction *Instr, return NoAliasLLVMValueSet; } +std::set +getCallToRetFlowValueSet(const llvm::Instruction *Instr, + IDEReachableAllocationSitesImpl &RASImpl, + const llvm::Value *Arg) { + const auto RASCallToRetFlowFunc = + RASImpl.getCallToRetFlowFunction(Instr, nullptr, {}); + const auto RASLLVMValueSet = RASCallToRetFlowFunc->computeTargets(Arg); + return RASLLVMValueSet; +} + std::string stringifyValueSet(const std::set &Vals) { std::string Ret; llvm::raw_string_ostream ROS(Ret); @@ -140,6 +196,8 @@ TEST(PureFlow, NormalFlow01) { "pure_flow/normal_flow/normal_flow_01_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); const auto *MainFunc = IRDB.getFunction("main"); // %0 @@ -166,10 +224,14 @@ TEST(PureFlow, NormalFlow01) { getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); EXPECT_EQ((std::set{PercentZero, Instr1}), getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr1}), + getNormalFlowValueSet(Instr8, RASImpl, PercentZero)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr8, AliasImpl, Instr1)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr8, NoAliasImpl, Instr1)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr8, RASImpl, Instr1)); // store ptr %1, ptr %.addr1, align 8 const auto *Instr10 = IRDB.getInstruction(10); @@ -178,10 +240,14 @@ TEST(PureFlow, NormalFlow01) { getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); EXPECT_EQ((std::set{PercentOne, Instr2}), getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr2}), + getNormalFlowValueSet(Instr10, RASImpl, PercentOne)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr10, AliasImpl, Instr2)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr10, NoAliasImpl, Instr2)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr10, RASImpl, Instr2)); // store ptr %One, ptr %OnePtr, align 8, !dbg !225 const auto *Instr17 = IRDB.getInstruction(17); @@ -190,10 +256,14 @@ TEST(PureFlow, NormalFlow01) { getNormalFlowValueSet(Instr17, AliasImpl, Instr3)); EXPECT_EQ((std::set{Instr3, Instr5}), getNormalFlowValueSet(Instr17, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr5}), + getNormalFlowValueSet(Instr17, RASImpl, Instr3)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr17, AliasImpl, Instr5)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr17, NoAliasImpl, Instr5)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr17, RASImpl, Instr5)); // store ptr %Two, ptr %TwoAddr, align 8, !dbg !228 const auto *Instr19 = IRDB.getInstruction(19); @@ -202,16 +272,22 @@ TEST(PureFlow, NormalFlow01) { getNormalFlowValueSet(Instr19, AliasImpl, Instr4)); EXPECT_EQ((std::set{Instr4, Instr6}), getNormalFlowValueSet(Instr19, NoAliasImpl, Instr4)); + EXPECT_EQ((std::set{Instr4, Instr6}), + getNormalFlowValueSet(Instr19, RASImpl, Instr4)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr19, AliasImpl, Instr6)); EXPECT_EQ(std::set{}, getNormalFlowValueSet(Instr19, NoAliasImpl, Instr6)); + EXPECT_EQ(std::set{}, + getNormalFlowValueSet(Instr19, RASImpl, Instr6)); // Other arg EXPECT_EQ(std::set{Instr19}, getNormalFlowValueSet(Instr19, AliasImpl, Instr19)); EXPECT_EQ(std::set{Instr19}, getNormalFlowValueSet(Instr19, NoAliasImpl, Instr19)); + EXPECT_EQ(std::set{Instr19}, + getNormalFlowValueSet(Instr19, RASImpl, Instr19)); } TEST(PureFlow, NormalFlow02) { @@ -219,6 +295,8 @@ TEST(PureFlow, NormalFlow02) { "pure_flow/normal_flow/normal_flow_02_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); const auto *MainFunc = IRDB.getFunction("main"); ASSERT_TRUE(MainFunc); @@ -242,10 +320,14 @@ TEST(PureFlow, NormalFlow02) { getNormalFlowValueSet(Instr8, AliasImpl, PercentZero)); EXPECT_EQ((std::set{PercentZero, Instr2}), getNormalFlowValueSet(Instr8, NoAliasImpl, PercentZero)); + EXPECT_EQ((std::set{PercentZero, Instr2}), + getNormalFlowValueSet(Instr8, RASImpl, PercentZero)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr8, AliasImpl, Instr2)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr8, NoAliasImpl, Instr2)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr8, RASImpl, Instr2)); // store ptr %1, ptr %.addr1, align 8, !psr.id !231; | ID: 10 const auto *Instr10 = IRDB.getInstruction(10); @@ -254,16 +336,22 @@ TEST(PureFlow, NormalFlow02) { getNormalFlowValueSet(Instr10, AliasImpl, PercentOne)); EXPECT_EQ((std::set{PercentOne, Instr3}), getNormalFlowValueSet(Instr10, NoAliasImpl, PercentOne)); + EXPECT_EQ((std::set{PercentOne, Instr3}), + getNormalFlowValueSet(Instr10, RASImpl, PercentOne)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr10, AliasImpl, Instr3)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr10, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr10, RASImpl, Instr3)); // Other arg EXPECT_EQ(std::set{Instr10}, getNormalFlowValueSet(Instr10, AliasImpl, Instr10)); EXPECT_EQ(std::set{Instr10}, getNormalFlowValueSet(Instr10, NoAliasImpl, Instr10)); + EXPECT_EQ(std::set{Instr10}, + getNormalFlowValueSet(Instr10, RASImpl, Instr10)); } TEST(PureFlow, NormalFlow03) { @@ -271,6 +359,8 @@ TEST(PureFlow, NormalFlow03) { "pure_flow/normal_flow/normal_flow_03_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %One = alloca i32, align 4, !psr.id !222; | ID: 3 const auto *Instr3 = IRDB.getInstruction(3); @@ -286,6 +376,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr18, AliasImpl, Instr3)); EXPECT_EQ((std::set{Instr3, Instr18}), getNormalFlowValueSet(Instr18, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr18}), + getNormalFlowValueSet(Instr18, RASImpl, Instr3)); // %3 = load i32, ptr %One, align 4, !dbg !251, !psr.id !252; | ID: 21 const auto *Instr21 = IRDB.getInstruction(21); ASSERT_TRUE(Instr21); @@ -293,6 +385,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr21, AliasImpl, Instr3)); EXPECT_EQ((std::set{Instr3, Instr21}), getNormalFlowValueSet(Instr21, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr21}), + getNormalFlowValueSet(Instr21, RASImpl, Instr3)); // %tobool = icmp ne i32 %3, 0, !dbg !251, !psr.id !253; | ID: 22 const auto *Instr22 = IRDB.getInstruction(22); @@ -304,6 +398,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr23, AliasImpl, Instr22)); EXPECT_EQ((std::set{Instr22, Instr23}), getNormalFlowValueSet(Instr23, NoAliasImpl, Instr22)); + EXPECT_EQ((std::set{Instr22, Instr23}), + getNormalFlowValueSet(Instr23, RASImpl, Instr22)); // %4 = load i32, ptr %One, align 4, !dbg !261, !psr.id !262; | ID: 27 const auto *Instr27 = IRDB.getInstruction(27); @@ -312,6 +408,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr27, AliasImpl, Instr3)); EXPECT_EQ((std::set{Instr3, Instr27}), getNormalFlowValueSet(Instr27, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr27}), + getNormalFlowValueSet(Instr27, RASImpl, Instr3)); // %5 = load i32, ptr %One, align 4, !dbg !263, !psr.id !264; | ID: 28 const auto *Instr28 = IRDB.getInstruction(28); @@ -320,6 +418,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr28, AliasImpl, Instr3)); EXPECT_EQ((std::set{Instr3, Instr28}), getNormalFlowValueSet(Instr28, NoAliasImpl, Instr3)); + EXPECT_EQ((std::set{Instr3, Instr28}), + getNormalFlowValueSet(Instr28, RASImpl, Instr3)); // %add = add nsw i32 %4, %5, !dbg !265, !psr.id !266; | ID: 29 const auto *Instr29 = IRDB.getInstruction(29); @@ -328,10 +428,14 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr29, AliasImpl, Instr27)); EXPECT_EQ((std::set{Instr27, Instr29}), getNormalFlowValueSet(Instr29, NoAliasImpl, Instr27)); + EXPECT_EQ((std::set{Instr27, Instr29}), + getNormalFlowValueSet(Instr29, RASImpl, Instr27)); EXPECT_EQ((std::set{Instr28, Instr29}), getNormalFlowValueSet(Instr29, AliasImpl, Instr28)); EXPECT_EQ((std::set{Instr28, Instr29}), getNormalFlowValueSet(Instr29, NoAliasImpl, Instr28)); + EXPECT_EQ((std::set{Instr28, Instr29}), + getNormalFlowValueSet(Instr29, RASImpl, Instr28)); // %One2 = getelementptr inbounds %struct.StructOne, ptr %ForGEP, i32 0, i32 // 0, !dbg !282, !psr.id !283; | ID: 37 @@ -341,6 +445,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr37, AliasImpl, Instr8)); EXPECT_EQ((std::set{Instr8, Instr37}), getNormalFlowValueSet(Instr37, NoAliasImpl, Instr8)); + EXPECT_EQ((std::set{Instr8, Instr37}), + getNormalFlowValueSet(Instr37, RASImpl, Instr8)); // %6 = load i32, ptr %One2, align 4, !dbg !282, !psr.id !284; | ID: 38 const auto *Instr38 = IRDB.getInstruction(38); @@ -349,6 +455,8 @@ TEST(PureFlow, NormalFlow03) { getNormalFlowValueSet(Instr38, AliasImpl, Instr37)); EXPECT_EQ((std::set{Instr37, Instr38}), getNormalFlowValueSet(Instr38, NoAliasImpl, Instr37)); + EXPECT_EQ((std::set{Instr37, Instr38}), + getNormalFlowValueSet(Instr38, RASImpl, Instr37)); } TEST(PureFlow, NormalFlow04) { @@ -356,6 +464,8 @@ TEST(PureFlow, NormalFlow04) { "pure_flow/normal_flow/normal_flow_04_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %Deref1 = alloca i32, align 4, !psr.id !222; | ID: 7 const auto *Instr7 = IRDB.getInstruction(7); @@ -383,10 +493,14 @@ TEST(PureFlow, NormalFlow04) { getNormalFlowValueSet(Instr26, AliasImpl, Instr25)); EXPECT_EQ((std::set{Instr25, Instr7}), getNormalFlowValueSet(Instr26, NoAliasImpl, Instr25)); + EXPECT_EQ((std::set{Instr25, Instr7}), + getNormalFlowValueSet(Instr26, RASImpl, Instr25)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr26, AliasImpl, Instr7)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr26, NoAliasImpl, Instr7)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr26, RASImpl, Instr7)); // store i32 %6, ptr %Deref2, align 4, !dbg !262, !psr.id !270; | ID: 31 const auto *Instr31 = IRDB.getInstruction(31); @@ -395,10 +509,14 @@ TEST(PureFlow, NormalFlow04) { getNormalFlowValueSet(Instr31, AliasImpl, Instr30)); EXPECT_EQ((std::set{Instr8, Instr30}), getNormalFlowValueSet(Instr31, NoAliasImpl, Instr30)); + EXPECT_EQ((std::set{Instr8, Instr30}), + getNormalFlowValueSet(Instr31, RASImpl, Instr30)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr31, AliasImpl, Instr8)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr31, NoAliasImpl, Instr8)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr31, RASImpl, Instr8)); // store i32 %10, ptr %Deref3, align 4, !dbg !272, !psr.id !282; | ID: 37 const auto *Instr37 = IRDB.getInstruction(37); @@ -407,10 +525,14 @@ TEST(PureFlow, NormalFlow04) { getNormalFlowValueSet(Instr37, AliasImpl, Instr36)); EXPECT_EQ((std::set{Instr9, Instr36}), getNormalFlowValueSet(Instr37, NoAliasImpl, Instr36)); + EXPECT_EQ((std::set{Instr9, Instr36}), + getNormalFlowValueSet(Instr37, RASImpl, Instr36)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr37, AliasImpl, Instr9)); EXPECT_EQ((std::set{}), getNormalFlowValueSet(Instr37, NoAliasImpl, Instr9)); + EXPECT_EQ((std::set{}), + getNormalFlowValueSet(Instr37, RASImpl, Instr9)); } /* @@ -422,6 +544,8 @@ TEST(PureFlow, CallFlow01) { "pure_flow/call_flow/call_flow_01_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // call void @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg !261, !psr.id // !262; | ID: 26 @@ -446,6 +570,12 @@ TEST(PureFlow, CallFlow01) { EXPECT_EQ(std::set{Param1}, getCallFlowValueSet(Instr26, NoAliasImpl, CallSite->getArgOperand(1), FuncForInstr26)); + EXPECT_EQ(std::set{Param0}, + getCallFlowValueSet(Instr26, RASImpl, CallSite->getArgOperand(0), + FuncForInstr26)); + EXPECT_EQ(std::set{Param1}, + getCallFlowValueSet(Instr26, RASImpl, CallSite->getArgOperand(1), + FuncForInstr26)); } else { FAIL(); } @@ -456,6 +586,8 @@ TEST(PureFlow, CallFlow02) { "pure_flow/call_flow/call_flow_02_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %One = alloca i32, align 4, !psr.id !251; | ID: 17 const auto *Instr17 = IRDB.getInstruction(17); @@ -508,6 +640,9 @@ TEST(PureFlow, CallFlow02) { EXPECT_EQ((std::set{CallFunc->getArg(0)}), getCallFlowValueSet(Instr50, NoAliasImpl, CSCallFunc->getArgOperand(0), CallFunc)); + EXPECT_EQ((std::set{CallFunc->getArg(0)}), + getCallFlowValueSet(Instr50, RASImpl, CSCallFunc->getArgOperand(0), + CallFunc)); EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), getCallFlowValueSet(Instr54, AliasImpl, @@ -517,6 +652,10 @@ TEST(PureFlow, CallFlow02) { getCallFlowValueSet(Instr54, NoAliasImpl, CSSecondCallFunc->getArgOperand(0), SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(0)}), + getCallFlowValueSet(Instr54, RASImpl, + CSSecondCallFunc->getArgOperand(0), + SecondCallFunc)); EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), getCallFlowValueSet(Instr54, AliasImpl, CSSecondCallFunc->getArgOperand(1), @@ -525,6 +664,10 @@ TEST(PureFlow, CallFlow02) { getCallFlowValueSet(Instr54, NoAliasImpl, CSSecondCallFunc->getArgOperand(1), SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(1)}), + getCallFlowValueSet(Instr54, RASImpl, + CSSecondCallFunc->getArgOperand(1), + SecondCallFunc)); EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), getCallFlowValueSet(Instr54, AliasImpl, CSSecondCallFunc->getArgOperand(2), @@ -533,6 +676,10 @@ TEST(PureFlow, CallFlow02) { getCallFlowValueSet(Instr54, NoAliasImpl, CSSecondCallFunc->getArgOperand(2), SecondCallFunc)); + EXPECT_EQ((std::set{SecondCallFunc->getArg(2)}), + getCallFlowValueSet(Instr54, RASImpl, + CSSecondCallFunc->getArgOperand(2), + SecondCallFunc)); } /* @@ -544,6 +691,8 @@ TEST(PureFlow, RetFlow01) { "pure_flow/ret_flow/ret_flow_01_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %Two = alloca i32, align 4, !psr.id !251; | ID: 20 const auto *UnusedValue = IRDB.getValueFromId(20); @@ -559,6 +708,8 @@ TEST(PureFlow, RetFlow01) { getRetFlowValueSet(Instr9, AliasImpl, UnusedValue, Instr0)); EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr9, NoAliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr9, RASImpl, UnusedValue, Instr0)); // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg // !281, !psr.id !282; | ID: 36 @@ -581,6 +732,12 @@ TEST(PureFlow, RetFlow01) { EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr36, NoAliasImpl, FuncZ4callii->getArg(1), Instr14)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, RASImpl, FuncZ4callii->getArg(0), Instr14)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr36, RASImpl, FuncZ4callii->getArg(1), Instr14)); // negative tests EXPECT_EQ( @@ -589,6 +746,9 @@ TEST(PureFlow, RetFlow01) { EXPECT_EQ( std::set{}, getRetFlowValueSet(Instr9, NoAliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr9, RASImpl, FuncZ4callii->getArg(1), Instr0)); } TEST(PureFlow, RetFlow02) { @@ -596,6 +756,8 @@ TEST(PureFlow, RetFlow02) { "pure_flow/ret_flow/ret_flow_02_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %Two = alloca i32, align 4, !psr.id !268; | ID: 29 const auto *UnusedValue = IRDB.getValueFromId(29); @@ -611,6 +773,8 @@ TEST(PureFlow, RetFlow02) { getRetFlowValueSet(Instr14, AliasImpl, UnusedValue, Instr0)); EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr14, NoAliasImpl, UnusedValue, Instr0)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr14, RASImpl, UnusedValue, Instr0)); // %call1 = call noundef i32 @_Z8newThreev(), !dbg !247, !psr.id !248; | ID: // 18 @@ -624,6 +788,8 @@ TEST(PureFlow, RetFlow02) { getRetFlowValueSet(Instr18, AliasImpl, UnusedValue, Instr4)); EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr18, NoAliasImpl, UnusedValue, Instr4)); + EXPECT_EQ(std::set{}, + getRetFlowValueSet(Instr18, RASImpl, UnusedValue, Instr4)); // %call = call noundef i32 @_Z4callii(i32 noundef %2, i32 noundef %3), !dbg // !298, !psr.id !299; | ID: 45 @@ -646,6 +812,12 @@ TEST(PureFlow, RetFlow02) { EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr45, NoAliasImpl, FuncZ4callii->getArg(1), Instr23)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, RASImpl, FuncZ4callii->getArg(0), Instr23)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr45, RASImpl, FuncZ4callii->getArg(1), Instr23)); // negative tests EXPECT_EQ( @@ -654,6 +826,9 @@ TEST(PureFlow, RetFlow02) { EXPECT_EQ(std::set{}, getRetFlowValueSet(Instr14, NoAliasImpl, FuncZ4callii->getArg(1), Instr0)); + EXPECT_EQ( + std::set{}, + getRetFlowValueSet(Instr14, RASImpl, FuncZ4callii->getArg(1), Instr0)); } TEST(PureFlow, RetFlow03) { @@ -661,6 +836,8 @@ TEST(PureFlow, RetFlow03) { "pure_flow/ret_flow/ret_flow_03_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // %ThreeInCall = alloca i32, align 4, !psr.id !254; | ID: 15 const auto *Instr15 = IRDB.getValueFromId(15); @@ -691,15 +868,17 @@ TEST(PureFlow, RetFlow03) { const auto &Got = getRetFlowValueSet( Instruction30, AliasImpl, FunctionZ8newThreePKi->getArg(0), Instruction9); + EXPECT_EQ(std::set{Instr29}, + getRetFlowValueSet(Instruction30, NoAliasImpl, + FunctionZ8newThreePKi->getArg(0), Instruction9)); + EXPECT_EQ((std::set{Instr29, Instr15, Instruction30}), + getRetFlowValueSet(Instruction30, RASImpl, + FunctionZ8newThreePKi->getArg(0), Instruction9)); EXPECT_EQ((std::set{Instr15, Instr16, Instr29, Instr30}), Got) << stringifyValueSet(Got); - EXPECT_EQ(std::set{Instr29}, - getRetFlowValueSet(Instruction30, NoAliasImpl, - FunctionZ8newThreePKi->getArg(0), Instruction9)); - // ret ptr @GlobalFour, !dbg !240, !psr.id !241; | ID: 10 const auto *Instruction10 = IRDB.getInstruction(10); ASSERT_TRUE(Instruction10); @@ -739,12 +918,18 @@ TEST(PureFlow, RetFlow03) { EXPECT_EQ(std::set{Instr52}, getRetFlowValueSet(Instruction68, NoAliasImpl, FuncZ4callRiPKi->getArg(0), Instruction48)); + EXPECT_EQ(std::set{Instr52}, + getRetFlowValueSet(Instruction68, RASImpl, + FuncZ4callRiPKi->getArg(0), Instruction48)); EXPECT_EQ(std::set{Instr53}, getRetFlowValueSet(Instruction68, AliasImpl, FuncZ4callRiPKi->getArg(1), Instruction48)); EXPECT_EQ(std::set{Instr53}, getRetFlowValueSet(Instruction68, NoAliasImpl, FuncZ4callRiPKi->getArg(1), Instruction48)); + EXPECT_EQ(std::set{Instr53}, + getRetFlowValueSet(Instruction68, RASImpl, + FuncZ4callRiPKi->getArg(1), Instruction48)); } /* @@ -757,6 +942,8 @@ TEST(PureFlow, CallToRetFlow01) { "pure_flow/call_to_ret_flow/call_to_ret_flow_01_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // store i32 0, ptr %Zero, align 4, !dbg !252, !psr.id !254; | ID: 22 const auto *Instr22 = IRDB.getInstruction(22); @@ -774,16 +961,22 @@ TEST(PureFlow, CallToRetFlow01) { getCallToRetFlowValueSet(Instr30, AliasImpl, Instr22)); EXPECT_EQ(std::set{Instr22}, getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr30, RASImpl, Instr22)); EXPECT_EQ(std::set{Instr24}, getCallToRetFlowValueSet(Instr30, AliasImpl, Instr24)); EXPECT_EQ(std::set{Instr24}, getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr24)); + EXPECT_EQ(std::set{Instr24}, + getCallToRetFlowValueSet(Instr30, RASImpl, Instr24)); EXPECT_EQ(std::set{Instr30}, getCallToRetFlowValueSet(Instr30, AliasImpl, Instr30)); EXPECT_EQ(std::set{Instr30}, getCallToRetFlowValueSet(Instr30, NoAliasImpl, Instr30)); + EXPECT_EQ(std::set{Instr30}, + getCallToRetFlowValueSet(Instr30, RASImpl, Instr30)); } TEST(PureFlow, CallToRetFlow02) { @@ -792,6 +985,8 @@ TEST(PureFlow, CallToRetFlow02) { "pure_flow/call_to_ret_flow/call_to_ret_flow_02_cpp_dbg.ll"}); IDEAliasImpl AliasImpl = IDEAliasImpl(&IRDB); IDENoAliasImpl NoAliasImpl = IDENoAliasImpl(&IRDB); + IDEReachableAllocationSitesImpl RASImpl = + IDEReachableAllocationSitesImpl(&IRDB); // store i32 3, ptr %Three, align 4, !dbg !223, !psr.id !225; | ID: 5 const auto *Instr5 = IRDB.getInstruction(5); @@ -809,11 +1004,15 @@ TEST(PureFlow, CallToRetFlow02) { getCallToRetFlowValueSet(Instr10, AliasImpl, Instr5)); EXPECT_EQ(std::set{Instr5}, getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr5)); + EXPECT_EQ(std::set{Instr5}, + getCallToRetFlowValueSet(Instr10, RASImpl, Instr5)); EXPECT_EQ(std::set{Instr8}, getCallToRetFlowValueSet(Instr10, AliasImpl, Instr8)); EXPECT_EQ(std::set{Instr8}, getCallToRetFlowValueSet(Instr10, NoAliasImpl, Instr8)); + EXPECT_EQ(std::set{Instr8}, + getCallToRetFlowValueSet(Instr10, RASImpl, Instr8)); // store i32 1, ptr %One, align 4, !dbg !255, !psr.id !257; | ID: 22 const auto *Instr22 = IRDB.getInstruction(22); @@ -831,16 +1030,22 @@ TEST(PureFlow, CallToRetFlow02) { getCallToRetFlowValueSet(Instr27, AliasImpl, Instr22)); EXPECT_EQ(std::set{Instr22}, getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr22)); + EXPECT_EQ(std::set{Instr22}, + getCallToRetFlowValueSet(Instr27, RASImpl, Instr22)); EXPECT_EQ(std::set{Instr25}, getCallToRetFlowValueSet(Instr27, AliasImpl, Instr25)); EXPECT_EQ(std::set{Instr25}, getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr25)); + EXPECT_EQ(std::set{Instr25}, + getCallToRetFlowValueSet(Instr27, RASImpl, Instr25)); EXPECT_EQ(std::set{Instr27}, getCallToRetFlowValueSet(Instr27, AliasImpl, Instr27)); EXPECT_EQ(std::set{Instr27}, getCallToRetFlowValueSet(Instr27, NoAliasImpl, Instr27)); + EXPECT_EQ(std::set{Instr27}, + getCallToRetFlowValueSet(Instr27, RASImpl, Instr27)); } }; // namespace From f5a9b43d9f9cfb2be70f7adc031046f2f7c5e121 Mon Sep 17 00:00:00 2001 From: Maximilian Leo Huber Date: Sat, 21 Jun 2025 18:39:34 +0000 Subject: [PATCH 11/24] Added c++ 20 modules support (#775) * Simple proof of concept * Extended POC * add hello-modules example tool + modules for phasar.db and phasar.control_flow * made hello_modules tool work * some meeting changes + rest of files * completed all cppm files * added missing folders * Seperated namespaces + bugfixes * added missing macro + removed internal * Simple proof of concept * Extended POC * add hello-modules example tool + modules for phasar.db and phasar.control_flow * made hello_modules tool work * some meeting changes + rest of files * completed all cppm files * added missing folders * Seperated namespaces + bugfixes * added missing macro + removed internal * Fix compilation in C++17 mode and fix some modules * Add phasar-svf to module phasar.llvm.pointer * Fix cyclic dependency between phasar_llvm and phasar_llvm_ifdside * Update README.md --------- Co-authored-by: Fabian Schiebel Co-authored-by: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> --- CMakeLists.txt | 2 + Config.cmake.in | 1 + README.md | 2 + cmake/phasar_macros.cmake | 28 ++- .../phasar/AnalysisStrategy/AnalysisSetup.h | 7 +- .../phasar/ControlFlow/SparseCFGProvider.h | 2 +- .../IfdsIde/Solver/StaticIDESolverConfig.h | 15 +- .../ControlFlow/SparseLLVMBasedICFGView.h | 6 +- include/phasar/TypeHierarchy/TypeHierarchy.h | 4 +- include/phasar/TypeHierarchy/VFTable.h | 4 +- include/phasar/Utils/TypeTraits.h | 8 +- lib/AnalysisStrategy/CMakeLists.txt | 7 +- lib/AnalysisStrategy/Strategies.cppm | 19 ++ lib/CMakeLists.txt | 41 ++-- lib/Config/CMakeLists.txt | 15 +- lib/Config/Config.cppm | 9 + lib/ControlFlow/CMakeLists.txt | 12 +- lib/ControlFlow/ControlFlow.cppm | 32 +++ lib/DB/CMakeLists.txt | 15 +- lib/DB/DB.cppm | 33 +++ lib/DataFlow/CMakeLists.txt | 17 ++ lib/DataFlow/DataFlow.cppm | 7 + lib/DataFlow/IfdsIde/CMakeLists.txt | 11 + lib/DataFlow/IfdsIde/IfdsIde.cppm | 122 +++++++++++ lib/DataFlow/Mono/CMakeLists.txt | 11 + lib/DataFlow/Mono/Mono.cppm | 20 ++ lib/DataFlow/PathSensitivity/CMakeLists.txt | 11 + .../PathSensitivity/PathSensitivity.cppm | 20 ++ lib/Domain/CMakeLists.txt | 11 + lib/Domain/Domain.cppm | 25 +++ lib/PhasarLLVM/CMakeLists.txt | 18 +- lib/PhasarLLVM/ControlFlow/CMakeLists.txt | 3 + lib/PhasarLLVM/ControlFlow/ControlFlow.cppm | 48 +++++ lib/PhasarLLVM/DB/CMakeLists.txt | 3 + lib/PhasarLLVM/DB/DB.cppm | 11 + lib/PhasarLLVM/DataFlow/CMakeLists.txt | 10 + lib/PhasarLLVM/DataFlow/DataFlow.cppm | 7 + .../DataFlow/IfdsIde/CMakeLists.txt | 4 +- lib/PhasarLLVM/DataFlow/IfdsIde/IfdsIde.cppm | 172 +++++++++++++++ lib/PhasarLLVM/DataFlow/Mono/CMakeLists.txt | 5 +- lib/PhasarLLVM/DataFlow/Mono/Mono.cppm | 32 +++ .../DataFlow/PathSensitivity/CMakeLists.txt | 3 + .../PathSensitivity/PathSensitivity.cppm | 14 ++ lib/PhasarLLVM/Domain/CMakeLists.txt | 8 + lib/PhasarLLVM/Domain/Domain.cppm | 11 + lib/PhasarLLVM/Passes/CMakeLists.txt | 3 + lib/PhasarLLVM/Passes/Passes.cppm | 15 ++ lib/PhasarLLVM/PhasarLLVM.cppm | 21 ++ lib/PhasarLLVM/Pointer/CMakeLists.txt | 3 + lib/PhasarLLVM/Pointer/Pointer.cppm | 36 ++++ lib/PhasarLLVM/TaintConfig/CMakeLists.txt | 3 + lib/PhasarLLVM/TaintConfig/TaintConfig.cppm | 23 ++ lib/PhasarLLVM/TypeHierarchy/CMakeLists.txt | 3 + .../TypeHierarchy/TypeHierarchy.cppm | 19 ++ lib/PhasarLLVM/Utils/CMakeLists.txt | 3 + lib/PhasarLLVM/Utils/Utils.cppm | 76 +++++++ lib/PhasarPass/CMakeLists.txt | 5 +- lib/PhasarPass/PhasarPass.cppm | 20 ++ lib/Pointer/CMakeLists.txt | 3 + lib/Pointer/PhasarPointer.cppm | 38 ++++ lib/TypeHierarchy/CMakeLists.txt | 9 + lib/TypeHierarchy/TypeHierarchy.cppm | 12 ++ lib/Utils/CMakeLists.txt | 3 + lib/Utils/Utils.cppm | 199 ++++++++++++++++++ tools/CMakeLists.txt | 3 + tools/hello-modules-tool/CMakeLists.txt | 18 ++ tools/hello-modules-tool/hello_modules.cpp | 65 ++++++ 67 files changed, 1392 insertions(+), 54 deletions(-) create mode 100644 lib/AnalysisStrategy/Strategies.cppm create mode 100644 lib/Config/Config.cppm create mode 100644 lib/ControlFlow/ControlFlow.cppm create mode 100644 lib/DB/DB.cppm create mode 100644 lib/DataFlow/CMakeLists.txt create mode 100644 lib/DataFlow/DataFlow.cppm create mode 100644 lib/DataFlow/IfdsIde/CMakeLists.txt create mode 100644 lib/DataFlow/IfdsIde/IfdsIde.cppm create mode 100644 lib/DataFlow/Mono/CMakeLists.txt create mode 100644 lib/DataFlow/Mono/Mono.cppm create mode 100644 lib/DataFlow/PathSensitivity/CMakeLists.txt create mode 100644 lib/DataFlow/PathSensitivity/PathSensitivity.cppm create mode 100644 lib/Domain/CMakeLists.txt create mode 100644 lib/Domain/Domain.cppm create mode 100644 lib/PhasarLLVM/ControlFlow/ControlFlow.cppm create mode 100644 lib/PhasarLLVM/DB/DB.cppm create mode 100644 lib/PhasarLLVM/DataFlow/DataFlow.cppm create mode 100644 lib/PhasarLLVM/DataFlow/IfdsIde/IfdsIde.cppm create mode 100644 lib/PhasarLLVM/DataFlow/Mono/Mono.cppm create mode 100644 lib/PhasarLLVM/DataFlow/PathSensitivity/PathSensitivity.cppm create mode 100644 lib/PhasarLLVM/Domain/CMakeLists.txt create mode 100644 lib/PhasarLLVM/Domain/Domain.cppm create mode 100644 lib/PhasarLLVM/Passes/Passes.cppm create mode 100644 lib/PhasarLLVM/PhasarLLVM.cppm create mode 100644 lib/PhasarLLVM/Pointer/Pointer.cppm create mode 100644 lib/PhasarLLVM/TaintConfig/TaintConfig.cppm create mode 100644 lib/PhasarLLVM/TypeHierarchy/TypeHierarchy.cppm create mode 100644 lib/PhasarLLVM/Utils/Utils.cppm create mode 100644 lib/PhasarPass/PhasarPass.cppm create mode 100644 lib/Pointer/PhasarPointer.cppm create mode 100644 lib/TypeHierarchy/CMakeLists.txt create mode 100644 lib/TypeHierarchy/TypeHierarchy.cppm create mode 100644 lib/Utils/Utils.cppm create mode 100644 tools/hello-modules-tool/CMakeLists.txt create mode 100644 tools/hello-modules-tool/hello_modules.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 5370c88ee3..58083e3b98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,8 @@ string(APPEND CMAKE_CXX_FLAGS_RELEASE "") option(CMAKE_VISIBILITY_INLINES_HIDDEN "Hide inlined functions from the DSO table (default ON)" ON) +option(PHASAR_BUILD_MODULES "Build C++20 modules for phasar" OFF) + include(CheckCXXCompilerFlag) # Handle memory issues with linking diff --git a/Config.cmake.in b/Config.cmake.in index fce796ba73..ecdb47dbc9 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -16,6 +16,7 @@ set(PHASAR_USE_LLVM_FAT_LIB @USE_LLVM_FAT_LIB@) set(PHASAR_BUILD_DYNLIB @PHASAR_BUILD_DYNLIB@) set(PHASAR_USE_Z3 @PHASAR_USE_Z3@) set(PHASAR_HAS_SQLITE @PHASAR_HAS_SQLITE@) +set(PHASAR_BUILD_MODULES @PHASAR_BUILD_MODULES@) if (PHASAR_USE_Z3) find_dependency(Z3 REQUIRED) diff --git a/README.md b/README.md index 17396e5464..d5a5538a7b 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,8 @@ PhASAR requires at least C++-17. However, building in C++20 mode is supported. You may enable this setting the cmake variable `CMAKE_CXX_STANDARD` to `20`. Although phasar currently does not make use of C++-20 features (except for some `concept`s behind an #ifdef border), your client application that just *uses* phasar as a library may want to use C++20 earlier. +**NEW**: PhASAR supports C++20 modules as an experimental feature. + ## Currently Supported Version of LLVM PhASAR is currently set up to support LLVM-15.0.* diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index c991f508db..2bd952852c 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -241,7 +241,7 @@ endmacro(add_phasar_executable) function(add_phasar_library name) set(PHASAR_LIB_OPTIONS SHARED STATIC MODULE INTERFACE) - set(PHASAR_LIB_MULTIVAL LLVM_LINK_COMPONENTS LINKS LINK_PUBLIC LINK_PRIVATE FILES) + set(PHASAR_LIB_MULTIVAL LLVM_LINK_COMPONENTS LINKS LINK_PUBLIC LINK_PRIVATE FILES MODULE_FILES) cmake_parse_arguments(PHASAR_LIB "${PHASAR_LIB_OPTIONS}" "" "${PHASAR_LIB_MULTIVAL}" ${ARGN}) set(srcs ${PHASAR_LIB_UNPARSED_ARGUMENTS}) list(APPEND srcs ${PHASAR_LIB_FILES}) @@ -277,6 +277,28 @@ function(add_phasar_library name) target_compile_features(${name} PUBLIC cxx_std_17) + set(install_module) + if(PHASAR_LIB_MODULE_FILES) + if(PHASAR_BUILD_MODULES) + target_sources(${name} PUBLIC + FILE_SET cxx_modules + TYPE CXX_MODULES + FILES ${PHASAR_LIB_MODULE_FILES} + ) + + target_compile_features(${name} PUBLIC cxx_std_20) + + set(install_module FILE_SET cxx_modules DESTINATION ${CMAKE_INSTALL_LIBDIR}) + elseif(NOT srcs) + # Add dummy src to prevent cmake error + set(dummy_src "${CMAKE_CURRENT_BINARY_DIR}/${name}_dummysrc.cpp") + if(NOT EXISTS "${dummy_src}") + file(WRITE "${dummy_src}" "") + endif() + target_sources(${name} PRIVATE "${dummy_src}") + endif() + endif() + if(LLVM_COMMON_DEPENDS) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) @@ -316,13 +338,15 @@ function(add_phasar_library name) EXPORT LLVMExports LIBRARY DESTINATION lib ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ${install_module} ) else() install(TARGETS ${name} EXPORT PhasarExports - # NOTE: Library, archive and runtime destination are automatically set by # GNUInstallDirs which is included in the top-level CMakeLists.txt + + ${install_module} ) endif() diff --git a/include/phasar/AnalysisStrategy/AnalysisSetup.h b/include/phasar/AnalysisStrategy/AnalysisSetup.h index 7b40140d4c..42005c36d2 100644 --- a/include/phasar/AnalysisStrategy/AnalysisSetup.h +++ b/include/phasar/AnalysisStrategy/AnalysisSetup.h @@ -10,11 +10,10 @@ #ifndef PHASAR_ANALYSISSTRATEGY_ANALYSISSETUP_H #define PHASAR_ANALYSISSTRATEGY_ANALYSISSETUP_H -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" -#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" -#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" - namespace psr { +class LLVMAliasSet; +class LLVMBasedICFG; +class DIBasedTypeHierarchy; /// Indicates that an analysis does not need a special configuration (file). struct HasNoConfigurationType {}; diff --git a/include/phasar/ControlFlow/SparseCFGProvider.h b/include/phasar/ControlFlow/SparseCFGProvider.h index 1ef2882f6a..e048310310 100644 --- a/include/phasar/ControlFlow/SparseCFGProvider.h +++ b/include/phasar/ControlFlow/SparseCFGProvider.h @@ -48,7 +48,7 @@ struct has_getSparseCFG< template // NOLINTNEXTLINE -static constexpr bool has_getSparseCFG_v = has_getSparseCFG::value; +constexpr bool has_getSparseCFG_v = has_getSparseCFG::value; } // namespace psr #endif // PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h index 1dc2316524..42b882b27e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h @@ -25,16 +25,15 @@ enum class JumpFunctionGCMode { struct IDESolverConfigBase { template - static inline constexpr bool - IsSimple1d = sizeof(std::pair) <= 32 && - std::is_nothrow_move_constructible_v - &&std::is_nothrow_move_constructible_v - &&has_llvm_dense_map_info; + static inline constexpr bool IsSimple1d = + sizeof(std::pair) <= 32 && + std::is_nothrow_move_constructible_v && + std::is_nothrow_move_constructible_v && has_llvm_dense_map_info; template - static inline constexpr bool - IsSimpleVal = sizeof(T) <= 32 && std::is_nothrow_move_constructible_v - &&has_llvm_dense_map_info; + static inline constexpr bool IsSimpleVal = + sizeof(T) <= 32 && std::is_nothrow_move_constructible_v && + has_llvm_dense_map_info; template using map_t = std::conditional_t, DenseTable1d, diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h index 7fbbb65a5e..64010f2935 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H -#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_VIEW_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_VIEW_H #include "phasar/ControlFlow/CallGraph.h" #include "phasar/ControlFlow/ICFGBase.h" @@ -75,4 +75,4 @@ class SparseLLVMBasedICFGView }; } // namespace psr -#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_VIEW_H diff --git a/include/phasar/TypeHierarchy/TypeHierarchy.h b/include/phasar/TypeHierarchy/TypeHierarchy.h index 5d06705d1b..6c54407cd8 100644 --- a/include/phasar/TypeHierarchy/TypeHierarchy.h +++ b/include/phasar/TypeHierarchy/TypeHierarchy.h @@ -42,8 +42,8 @@ template class TypeHierarchy { }; template -static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const TypeHierarchy &TH) { +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const TypeHierarchy &TH) { TH.print(OS); return OS; } diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index 4fc331cf53..62eb4432af 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -41,8 +41,8 @@ template class VFTable { }; template -static inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const VFTable &Table) { +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const VFTable &Table) { Table.print(OS); return OS; } diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 7f0023af2f..1e2f2524cc 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -28,7 +28,9 @@ namespace psr { #if __cplusplus < 202002L -template struct type_identity { using type = T; }; +template struct type_identity { + using type = T; +}; #else template using type_identity = std::type_identity; #endif @@ -275,12 +277,12 @@ PSR_CONCEPT has_isInteresting_v = // NOLINT detail::has_isInteresting::value; template -static constexpr bool has_llvm_dense_map_info = +constexpr bool has_llvm_dense_map_info = detail::has_llvm_dense_map_info::value; template using type_identity_t = typename type_identity::type; template -static constexpr size_t variant_idx = detail::variant_idx::value; +constexpr size_t variant_idx = detail::variant_idx::value; template using ElementType = typename detail::ElementType::type; diff --git a/lib/AnalysisStrategy/CMakeLists.txt b/lib/AnalysisStrategy/CMakeLists.txt index 8a194cc68a..3ecd9f9c15 100644 --- a/lib/AnalysisStrategy/CMakeLists.txt +++ b/lib/AnalysisStrategy/CMakeLists.txt @@ -2,5 +2,10 @@ file(GLOB_RECURSE ANALYSIS_STRATEGY_SRC *.h *.cpp) add_phasar_library(phasar_analysis_strategy ${ANALYSIS_STRATEGY_SRC} - LLVM_LINK_COMPONENTS Support + + LLVM_LINK_COMPONENTS + Support + + MODULE_FILES + Strategies.cppm ) diff --git a/lib/AnalysisStrategy/Strategies.cppm b/lib/AnalysisStrategy/Strategies.cppm new file mode 100644 index 0000000000..6b776ede28 --- /dev/null +++ b/lib/AnalysisStrategy/Strategies.cppm @@ -0,0 +1,19 @@ +module; + +#include "phasar/AnalysisStrategy.h" + +export module phasar.analysisstrategy; + +export namespace psr { +using psr::AnalysisSetup; +using psr::AnalysisStrategy; +using psr::DefaultAnalysisSetup; +using psr::DemandDrivenAnalysis; +using psr::HasNoConfigurationType; +using psr::IncrementalUpdateAnalysis; +using psr::ModuleWiseAnalysis; +using psr::toAnalysisStrategy; +using psr::toString; +using psr::VariationalAnalysis; +using psr::operator<<; +} // namespace psr diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index ffe1291f3b..3351b1c590 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,11 +1,15 @@ add_subdirectory(AnalysisStrategy) +add_subdirectory(Config) +add_subdirectory(ControlFlow) +add_subdirectory(DataFlow) +add_subdirectory(DB) +add_subdirectory(Domain) add_subdirectory(PhasarLLVM) add_subdirectory(PhasarPass) -add_subdirectory(DB) -add_subdirectory(Config) -add_subdirectory(Utils) add_subdirectory(Pointer) -add_subdirectory(ControlFlow) +add_subdirectory(TypeHierarchy) +add_subdirectory(Utils) + if(BUILD_PHASAR_CLANG) if(PHASAR_IN_TREE) add_dependencies(phasar_interface intrinsics_gen) @@ -18,23 +22,34 @@ if(PHASAR_BUILD_DYNLIB) endif() set(PHASAR_LINK_LIBS - phasar_utils - phasar_passes + phasar_analysis_strategy phasar_config - phasar_pointer phasar_controlflow + phasar_dataflow + phasar_dataflow_ifdside + phasar_dataflow_mono + phasar_dataflow_pathsensitivity + phasar_db + phasar_domain - phasar_llvm_utils + phasar_llvm + phasar_llvm_controlflow + phasar_llvm_ifdside + phasar_llvm_mono + phasar_llvm_pathsensitivity + phasar_llvm_dataflow phasar_llvm_db + phasar_llvm_domain phasar_llvm_pointer phasar_llvm_typehierarchy - phasar_llvm_controlflow + phasar_llvm_utils + phasar_pass + phasar_passes + phasar_pointer phasar_taintconfig - phasar_mono - phasar_llvm - phasar_llvm_ifdside - phasar_analysis_strategy + phasar_typehierarchy + phasar_utils ) if(BUILD_PHASAR_CLANG) list(APPEND PHASAR_LINK_LIBS phasar_clang) diff --git a/lib/Config/CMakeLists.txt b/lib/Config/CMakeLists.txt index 455fa8fbf8..4c125ca131 100644 --- a/lib/Config/CMakeLists.txt +++ b/lib/Config/CMakeLists.txt @@ -2,7 +2,16 @@ file(GLOB_RECURSE CONFIG_SRC *.h *.cpp) add_phasar_library(phasar_config ${CONFIG_SRC} - LINKS phasar_utils - LINK_PRIVATE ${Boost_LIBRARIES} - LLVM_LINK_COMPONENTS Support + + LINKS + phasar_utils + + LINK_PRIVATE + ${Boost_LIBRARIES} + + LLVM_LINK_COMPONENTS + Support + + MODULE_FILES + Config.cppm ) diff --git a/lib/Config/Config.cppm b/lib/Config/Config.cppm new file mode 100644 index 0000000000..be5b46dfca --- /dev/null +++ b/lib/Config/Config.cppm @@ -0,0 +1,9 @@ +module; + +#include "phasar/Config.h" + +export module phasar.config; + +export namespace psr { +using psr::PhasarConfig; +} // namespace psr diff --git a/lib/ControlFlow/CMakeLists.txt b/lib/ControlFlow/CMakeLists.txt index d6f91baa6b..adebfe73ae 100644 --- a/lib/ControlFlow/CMakeLists.txt +++ b/lib/ControlFlow/CMakeLists.txt @@ -2,8 +2,16 @@ file(GLOB_RECURSE CONTROLFLOW_SRC *.h *.cpp) add_phasar_library(phasar_controlflow ${CONTROLFLOW_SRC} + LINKS phasar_utils - LLVM_LINK_COMPONENTS Support - LINK_PRIVATE nlohmann_json::nlohmann_json + + LLVM_LINK_COMPONENTS + Support + + LINK_PRIVATE + nlohmann_json::nlohmann_json + + MODULE_FILES + ControlFlow.cppm ) diff --git a/lib/ControlFlow/ControlFlow.cppm b/lib/ControlFlow/ControlFlow.cppm new file mode 100644 index 0000000000..04ae8d675d --- /dev/null +++ b/lib/ControlFlow/ControlFlow.cppm @@ -0,0 +1,32 @@ +module; + +#include "phasar/ControlFlow.h" +#include "phasar/ControlFlow/SparseCFGBase.h" +#include "phasar/ControlFlow/SparseCFGProvider.h" + +export module phasar.controlflow; + +export namespace psr { +using psr::CallGraph; +using psr::CallGraphAnalysisType; +using psr::CallGraphBuilder; +using psr::CGTraits; +using psr::toCallGraphAnalysisType; +using psr::toString; +using psr::operator<<; +using psr::CallGraphBase; +using psr::CallGraphData; +using psr::CFGBase; +using psr::CFGTraits; +using psr::has_getSparseCFG; +using psr::has_getSparseCFG_v; +using psr::ICFGBase; +using psr::is_cfg_v; +using psr::is_icfg_v; +using psr::is_sparse_cfg_v; +using psr::SparseCFGBase; +using psr::SparseCFGProvider; +using psr::SpecialMemberFunctionType; +using psr::toSpecialMemberFunctionType; +using psr::valueOf; +} // namespace psr diff --git a/lib/DB/CMakeLists.txt b/lib/DB/CMakeLists.txt index 74fef8d36a..5469d9933d 100644 --- a/lib/DB/CMakeLists.txt +++ b/lib/DB/CMakeLists.txt @@ -3,8 +3,17 @@ if(SQLite3_FOUND) add_phasar_library(phasar_db ${DB_SRC} - LINKS phasar_passes phasar_utils - LLVM_LINK_COMPONENTS Support - LINK_PRIVATE SQLite::SQLite3 + + LINKS phasar_passes + phasar_utils + + LLVM_LINK_COMPONENTS + Support + + LINK_PRIVATE + SQLite::SQLite3 + + MODULE_FILES + DB.cppm ) endif() diff --git a/lib/DB/DB.cppm b/lib/DB/DB.cppm new file mode 100644 index 0000000000..70c396ce87 --- /dev/null +++ b/lib/DB/DB.cppm @@ -0,0 +1,33 @@ +module; + +#include "phasar/DB/Hexastore.h" +#include "phasar/DB/ProjectIRDBBase.h" +#include "phasar/DB/Queries.h" + +export module phasar.db; + +export namespace psr { +using psr::IRDBGetFunctionDef; +using psr::ProjectIRDBBase; +using psr::ProjectIRDBTraits; + +#ifdef PHASAR_HAS_SQLITE +using psr::Hexastore; +using psr::HSResult; +using psr::INIT; +using psr::OPSInsert; +using psr::OSPInsert; +using psr::POSInsert; +using psr::PSOInsert; +using psr::SearchSPO; +using psr::SearchSPX; +using psr::SearchSXO; +using psr::SearchSXX; +using psr::SearchXPO; +using psr::SearchXPX; +using psr::SearchXXO; +using psr::SearchXXX; +using psr::SOPInsert; +using psr::SPOInsert; +#endif +} // namespace psr diff --git a/lib/DataFlow/CMakeLists.txt b/lib/DataFlow/CMakeLists.txt new file mode 100644 index 0000000000..8fbdcb465a --- /dev/null +++ b/lib/DataFlow/CMakeLists.txt @@ -0,0 +1,17 @@ +add_subdirectory(IfdsIde) +add_subdirectory(Mono) +add_subdirectory(PathSensitivity) + +file(GLOB_RECURSE DATAFLOW_SRC *.h *.cpp) + +add_phasar_library(phasar_dataflow + ${DATAFLOW_SRC} + + LINKS + phasar_dataflow_ifdside + phasar_dataflow_mono + phasar_dataflow_pathsensitivity + + MODULE_FILES + DataFlow.cppm +) diff --git a/lib/DataFlow/DataFlow.cppm b/lib/DataFlow/DataFlow.cppm new file mode 100644 index 0000000000..674406d5f4 --- /dev/null +++ b/lib/DataFlow/DataFlow.cppm @@ -0,0 +1,7 @@ +module; + +export module phasar.dataflow; + +export import phasar.dataflow.ifdside; +export import phasar.dataflow.mono; +export import phasar.dataflow.pathsensitivity; diff --git a/lib/DataFlow/IfdsIde/CMakeLists.txt b/lib/DataFlow/IfdsIde/CMakeLists.txt new file mode 100644 index 0000000000..763e5bb437 --- /dev/null +++ b/lib/DataFlow/IfdsIde/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE IFDSIDE_SRC *.h *.cpp) + +add_phasar_library(phasar_dataflow_ifdside + ${IFDSIDE_SRC} + + LINK_PRIVATE + nlohmann_json::nlohmann_json + + MODULE_FILES + IfdsIde.cppm +) diff --git a/lib/DataFlow/IfdsIde/IfdsIde.cppm b/lib/DataFlow/IfdsIde/IfdsIde.cppm new file mode 100644 index 0000000000..58d2744f7f --- /dev/null +++ b/lib/DataFlow/IfdsIde/IfdsIde.cppm @@ -0,0 +1,122 @@ +module; + +#include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionStats.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" +#include "phasar/DataFlow/IfdsIde/EntryPointUtils.h" +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" +#include "phasar/DataFlow/IfdsIde/GenericFlowFunction.h" +#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/Solver/Compressor.h" +#include "phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h" +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h" + +export module phasar.dataflow.ifdside; + +export namespace psr { +using psr::AllBottom; +using psr::AllTop; +using psr::CachedEdgeFunction; +using psr::ConstantEdgeFunction; +using psr::defaultComposeOrNull; +using psr::DefaultEdgeFunctionSingletonCache; +using psr::DefaultEdgeFunctionSingletonCacheImpl; +using psr::EdgeFunction; +using psr::EdgeFunctionAllocationPolicy; +using psr::EdgeFunctionBase; +using psr::EdgeFunctionRef; +using psr::EdgeFunctions; +using psr::EdgeFunctionSingletonCache; +using psr::EdgeFunctionStats; +using psr::EdgeIdentity; +using psr::IsEdgeFunction; +using psr::operator==; +using psr::operator<<; +using psr::addSeedsForStartingPoints; +using psr::AllTopFnProvider; +using psr::checkSREquality; +using psr::Compressor; +using psr::ConstantEdgeFunction; +using psr::defaultJoinOrNull; +using psr::DefaultMapKeyCompressor; +using psr::EdgeFunctionCache; +using psr::EdgeFunctionCacheStats; +using psr::EdgeFunctionComposer; +using psr::EdgeFunctionKind; +using psr::ESGEdgeKind; +using psr::FlowEdgeFunctionCache; +using psr::FlowEdgeFunctionCacheNG; +using psr::FlowEdgeFunctionCacheStats; +using psr::FlowFunction; +using psr::FlowFunctionCache; +using psr::FlowFunctionCacheStats; +using psr::FlowFunctionPtrTypeOf; +using psr::FlowFunctions; +using psr::FlowFunctionTemplates; +using psr::forallStartingPoints; +using psr::GenericFlowFunction; +using psr::GenericFlowFunctionView; +using psr::GenericSolverResults; +using psr::getMetaDataID; +using psr::IdBasedSolverResults; +using psr::IDESolver; +using psr::IDETabulationProblem; +using psr::ifdsEqual; +using psr::IFDSIDESolverConfig; +using psr::IFDSSolver; +using psr::IFDSTabulationProblem; +using psr::InitialSeeds; +using psr::IsFlowFunction; +using psr::JoinEdgeFunction; +using psr::LLVMMapKeyCompressor; +using psr::MapKeyCompressorCombinator; +using psr::NodeCompressorTraits; +using psr::NoneCompressor; +using psr::OwningSolverResults; +using psr::SolverConfigOptions; +using psr::SolverResults; +using psr::ValCompressorTraits; +using psr::ZeroedFlowFunction; +using psr::operator<<; +using psr::DefaultIDESolverConfig; +using psr::DequeWorkList; +using psr::IDESolverAPIMixin; +using psr::IDESolverConfigBase; +using psr::IterativeIDESolver; +using psr::IterativeIDESolverBase; +using psr::IterativeIDESolverStats; +using psr::IterIDEPropagationJob; +using psr::JumpFunctionGCMode; +using psr::JumpFunctions; +using psr::OwningSolverResults; +using psr::PathAwareIDESolver; +using psr::PathEdge; +using psr::SmallVectorWorkList; +using psr::solveIDEProblem; +using psr::solveIFDSProblem; +using psr::SolverStatsSelector; +using psr::VectorWorkList; +using psr::WithComputeValues; +using psr::WithEndSummaryTab; +using psr::WithGCMode; +using psr::WithStats; +using psr::WithWorkList; +} // namespace psr + +export namespace llvm { +using llvm::cast_convert_val; +using llvm::cast_or_null; +using llvm::cast_retty_impl; +using llvm::CastInfo; +using llvm::CastIsPossible; +using llvm::DenseMapInfo; +using llvm::dyn_cast_or_null; +using llvm::isa_impl_cl; +} // namespace llvm diff --git a/lib/DataFlow/Mono/CMakeLists.txt b/lib/DataFlow/Mono/CMakeLists.txt new file mode 100644 index 0000000000..abf6f6397a --- /dev/null +++ b/lib/DataFlow/Mono/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE MONO_SRC *.h *.cpp) + +add_phasar_library(phasar_dataflow_mono + ${MONO_SRC} + + LINK_PRIVATE + nlohmann_json::nlohmann_json + + MODULE_FILES + Mono.cppm +) diff --git a/lib/DataFlow/Mono/Mono.cppm b/lib/DataFlow/Mono/Mono.cppm new file mode 100644 index 0000000000..8d0f77d825 --- /dev/null +++ b/lib/DataFlow/Mono/Mono.cppm @@ -0,0 +1,20 @@ +module; + +#include "phasar/DataFlow/Mono/Contexts/CallStringCTX.h" +#include "phasar/DataFlow/Mono/InterMonoProblem.h" +#include "phasar/DataFlow/Mono/Solver/InterMonoSolver.h" +#include "phasar/DataFlow/Mono/Solver/IntraMonoSolver.h" + +export module phasar.dataflow.mono; + +export namespace psr { +using psr::CallStringCTX; +using psr::InterMonoProblem; +using psr::InterMonoSolver; +using psr::IntraMonoProblem; +using psr::IntraMonoSolver; +} // namespace psr + +export namespace std { +using std::hash; +} // namespace std diff --git a/lib/DataFlow/PathSensitivity/CMakeLists.txt b/lib/DataFlow/PathSensitivity/CMakeLists.txt new file mode 100644 index 0000000000..6ed81a3225 --- /dev/null +++ b/lib/DataFlow/PathSensitivity/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE PATHSENSITIVITY_SRC *.h *.cpp) + +add_phasar_library(phasar_dataflow_pathsensitivity + ${PATHSENSITIVITY_SRC} + + LINK_PRIVATE + nlohmann_json::nlohmann_json + + MODULE_FILES + PathSensitivity.cppm +) diff --git a/lib/DataFlow/PathSensitivity/PathSensitivity.cppm b/lib/DataFlow/PathSensitivity/PathSensitivity.cppm new file mode 100644 index 0000000000..f913d862af --- /dev/null +++ b/lib/DataFlow/PathSensitivity/PathSensitivity.cppm @@ -0,0 +1,20 @@ +module; + +#include "phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h" +#include "phasar/DataFlow/PathSensitivity/FlowPath.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManager.h" + +export module phasar.dataflow.pathsensitivity; + +export namespace psr { +using psr::ExplodedSuperGraph; +using psr::FlowPath; +using psr::is_pathtracingfilter_for; +using psr::PathSensitivityConfig; +using psr::PathSensitivityConfigBase; +using psr::PathSensitivityManager; +using psr::PathSensitivityManagerBase; +using psr::PathSensitivityManagerMixin; +using psr::PathTracingFilter; +} // namespace psr diff --git a/lib/Domain/CMakeLists.txt b/lib/Domain/CMakeLists.txt new file mode 100644 index 0000000000..2879357b44 --- /dev/null +++ b/lib/Domain/CMakeLists.txt @@ -0,0 +1,11 @@ +file(GLOB_RECURSE DOMAIN_SRC *.h *.cpp) + +add_phasar_library(phasar_domain + ${DOMAIN_SRC} + + LINK_PRIVATE + nlohmann_json::nlohmann_json + + MODULE_FILES + Domain.cppm +) diff --git a/lib/Domain/Domain.cppm b/lib/Domain/Domain.cppm new file mode 100644 index 0000000000..2b78a69370 --- /dev/null +++ b/lib/Domain/Domain.cppm @@ -0,0 +1,25 @@ +module; + +#include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Domain/LatticeDomain.h" + +export module phasar.domain; + +export namespace psr { +using psr::AnalysisDomain; +using psr::BinaryDomain; +using psr::to_string; +using psr::operator<<; +using psr::operator==; +using psr::operator<; +using psr::Bottom; +using psr::JoinLatticeTraits; +using psr::LatticeDomain; +using psr::NonTopBotValue; +using psr::Top; +} // namespace psr + +export namespace std { +using std::hash; +} // namespace std diff --git a/lib/PhasarLLVM/CMakeLists.txt b/lib/PhasarLLVM/CMakeLists.txt index be4adc5e0b..2a3908722b 100644 --- a/lib/PhasarLLVM/CMakeLists.txt +++ b/lib/PhasarLLVM/CMakeLists.txt @@ -1,6 +1,7 @@ -add_subdirectory(DB) add_subdirectory(ControlFlow) add_subdirectory(DataFlow) +add_subdirectory(DB) +add_subdirectory(Domain) add_subdirectory(Passes) add_subdirectory(Pointer) add_subdirectory(TaintConfig) @@ -13,13 +14,22 @@ add_phasar_library(phasar_llvm ${PHASAR_LLVM_SRC} LINKS - phasar_utils - phasar_llvm_pointer - phasar_llvm_db phasar_llvm_controlflow + phasar_llvm_dataflow + phasar_llvm_db + phasar_llvm_domain + phasar_llvm_ifdside + phasar_llvm_pointer phasar_llvm_typehierarchy + phasar_llvm_utils + phasar_passes + phasar_utils + phasar_taintconfig LLVM_LINK_COMPONENTS Core Support + + MODULE_FILES + PhasarLLVM.cppm ) diff --git a/lib/PhasarLLVM/ControlFlow/CMakeLists.txt b/lib/PhasarLLVM/ControlFlow/CMakeLists.txt index 0000f7b1ad..23c251cf49 100644 --- a/lib/PhasarLLVM/ControlFlow/CMakeLists.txt +++ b/lib/PhasarLLVM/ControlFlow/CMakeLists.txt @@ -13,4 +13,7 @@ add_phasar_library(phasar_llvm_controlflow Core Support Demangle + + MODULE_FILES + ControlFlow.cppm ) diff --git a/lib/PhasarLLVM/ControlFlow/ControlFlow.cppm b/lib/PhasarLLVM/ControlFlow/ControlFlow.cppm new file mode 100644 index 0000000000..271967a5b2 --- /dev/null +++ b/lib/PhasarLLVM/ControlFlow/ControlFlow.cppm @@ -0,0 +1,48 @@ +module; + +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h" + +export module phasar.llvm.controlflow; + +export namespace psr { +using psr::buildLLVMBasedCallGraph; +using psr::CFGTraits; +using psr::CHAResolver; +using psr::getEntryFunctions; +using psr::getEntryFunctionsMut; +using psr::getNonPureVirtualVFTEntry; +using psr::getReceiverType; +using psr::getReceiverTypeName; +using psr::getVFTIndex; +using psr::GlobalCtorsDtorsModel; +using psr::ICFGBase; +using psr::isConsistentCall; +using psr::isHeapAllocatingFunction; +using psr::isVirtualCall; +using psr::LLVMBasedBackwardCFG; +using psr::LLVMBasedBackwardICFG; +using psr::LLVMBasedCallGraph; +using psr::LLVMBasedCFG; +using psr::LLVMBasedICFG; +using psr::LLVMVFTableProvider; +using psr::NOResolver; +using psr::OTFResolver; +using psr::Resolver; +using psr::RTAResolver; +using psr::SparseLLVMBasedCFG; +using psr::SparseLLVMBasedCFGProvider; +using psr::SparseLLVMBasedICFG; +using psr::SparseLLVMBasedICFGView; +using psr::valueOf; +} // namespace psr diff --git a/lib/PhasarLLVM/DB/CMakeLists.txt b/lib/PhasarLLVM/DB/CMakeLists.txt index bb6e3c3960..062af754c8 100644 --- a/lib/PhasarLLVM/DB/CMakeLists.txt +++ b/lib/PhasarLLVM/DB/CMakeLists.txt @@ -11,4 +11,7 @@ add_phasar_library(phasar_llvm_db Core Support IRReader + + MODULE_FILES + DB.cppm ) diff --git a/lib/PhasarLLVM/DB/DB.cppm b/lib/PhasarLLVM/DB/DB.cppm new file mode 100644 index 0000000000..ec8a41b961 --- /dev/null +++ b/lib/PhasarLLVM/DB/DB.cppm @@ -0,0 +1,11 @@ +module; + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" + +export module phasar.llvm.db; + +export namespace psr { +using psr::fromMetaDataId; +using psr::LLVMProjectIRDB; +using psr::ProjectIRDBTraits; +} // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/CMakeLists.txt b/lib/PhasarLLVM/DataFlow/CMakeLists.txt index 3af5da4864..06e922a93e 100644 --- a/lib/PhasarLLVM/DataFlow/CMakeLists.txt +++ b/lib/PhasarLLVM/DataFlow/CMakeLists.txt @@ -1,3 +1,13 @@ add_subdirectory(IfdsIde) add_subdirectory(Mono) add_subdirectory(PathSensitivity) + +add_phasar_library(phasar_llvm_dataflow + LINKS + phasar_llvm_ifdside + phasar_llvm_mono + phasar_llvm_pathsensitivity + + MODULE_FILES + DataFlow.cppm +) diff --git a/lib/PhasarLLVM/DataFlow/DataFlow.cppm b/lib/PhasarLLVM/DataFlow/DataFlow.cppm new file mode 100644 index 0000000000..5a769f0546 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/DataFlow.cppm @@ -0,0 +1,7 @@ +module; + +export module phasar.llvm.dataflow; + +export import phasar.llvm.dataflow.ifdside; +export import phasar.llvm.dataflow.mono; +export import phasar.llvm.dataflow.pathsensitivity; diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt b/lib/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt index 327eb03afd..4c66687fb5 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt @@ -7,7 +7,6 @@ add_phasar_library(phasar_llvm_ifdside phasar_config phasar_utils phasar_llvm_pointer - phasar_llvm phasar_llvm_typehierarchy phasar_llvm_controlflow phasar_llvm_utils @@ -20,4 +19,7 @@ add_phasar_library(phasar_llvm_ifdside LINK_PRIVATE ${Boost_LIBRARIES} + + MODULE_FILES + IfdsIde.cppm ) diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/IfdsIde.cppm b/lib/PhasarLLVM/DataFlow/IfdsIde/IfdsIde.cppm new file mode 100644 index 0000000000..94a9f38607 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/IfdsIde.cppm @@ -0,0 +1,172 @@ +module; + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/ComposeEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/KillIfSanitizedEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/ConstantHelper.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/LCAEdgeFunctionComposer.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCalleeFlowFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCallerFlowFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/TypecastEdgeFunction.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEProtoAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSProtoAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSignAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h" + +export module phasar.llvm.dataflow.ifdside; + +export namespace psr { +using psr::AbstractMemoryLocation; +using psr::AbstractMemoryLocationFactory; +using psr::BasicBlockOrdering; +using psr::DToString; +using psr::FeatureTaintGenerator; +using psr::hash_value; +using psr::IDEExtendedTaintAnalysis; +using psr::IDEExtendedTaintAnalysisDomain; +using psr::IDEFeatureTaintAnalysis; +using psr::IDEFeatureTaintAnalysisDomain; +using psr::IDEFeatureTaintEdgeFact; +using psr::IDEIIAFlowFact; +using psr::IDELinearConstantAnalysis; +using psr::IFDSSolverTest; +using psr::JoinLatticeTraits; +using psr::LToString; +using psr::NonTopBotValue; +using psr::operator<<; +using psr::DefaultAliasAwareIDEProblem; +using psr::DefaultAliasAwareIFDSProblem; +using psr::DefaultNoAliasIDEProblem; +using psr::DefaultNoAliasIFDSProblem; +using psr::DToString; +using psr::IDEExtendedTaintAnalysis; +using psr::IDEInstInteractionAnalysis; +using psr::IDEInstInteractionAnalysisDomain; +using psr::IDEInstInteractionAnalysisT; +using psr::IDELinearConstantAnalysis; +using psr::IDELinearConstantAnalysisDomain; +using psr::IDEProtoAnalysis; +using psr::IDEProtoAnalysisDomain; +using psr::IDESecureHeapPropagation; +using psr::IDESecureHeapPropagationAnalysisDomain; +using psr::IDESolverTest; +using psr::IDESolverTestAnalysisDomain; +using psr::IDETypeStateAnalysis; +using psr::IDETypeStateAnalysisDomain; +using psr::IFDSConstAnalysis; +using psr::IFDSProtoAnalysis; +using psr::IFDSSignAnalysis; +using psr::IFDSSolverTest; +using psr::IFDSTaintAnalysis; +using psr::IFDSTypeAnalysis; +using psr::IFDSUninitializedVariables; +using psr::JoinLatticeTraits; +using psr::LLVMZeroValue; +using psr::LToString; +using psr::mapFactsAlongsideCallSite; +using psr::mapFactsToCallee; +using psr::mapFactsToCaller; +using psr::SecureHeapFact; +using psr::SecureHeapValue; +using psr::strongUpdateStore; +using psr::operator<<; +using psr::operator==; +using psr::CSTDFILEIOState; +using psr::EdgeFunctionBase; +using psr::IDEGeneralizedLCA; +using psr::IDEGeneralizedLCADomain; +using psr::JoinLatticeTraits; +using psr::to_string; +using psr::operator<<; +using psr::CSTDFILEIOTypeStateDescription; +using psr::getLibCSummary; +using psr::IDETypeStateAnalysis; +using psr::OpenSSLEVPKDFCTXDescription; +using psr::OpenSSLEVPKDFCTXState; +using psr::OpenSSLEVPKDFDescription; +using psr::OpenSSLEVPKDFState; +using psr::OpenSSLSecureHeapDescription; +using psr::OpenSSLSecureHeapState; +using psr::OpenSSLSecureMemoryDescription; +using psr::OpenSSLSecureMemoryState; +using psr::to_string; +using psr::TypeStateDescription; +using psr::TypeStateDescriptionBase; +} // namespace psr + +export namespace std { +using std::hash; +} // namespace std + +export namespace psr::library_summary { +using psr::library_summary::DataFlowFact; +using psr::library_summary::FunctionDataFlowFacts; +using psr::library_summary::LLVMFunctionDataFlowFacts; +using psr::library_summary::Parameter; +using psr::library_summary::readFromFDFF; +using psr::library_summary::ReturnValue; +} // namespace psr::library_summary + +export namespace llvm { +using llvm::DenseMapInfo; +} // namespace llvm + +export namespace psr::XTaint { +using psr::XTaint::AnalysisBase; +using psr::XTaint::ComposeEdgeFunction; +using psr::XTaint::EdgeDomain; +using psr::XTaint::KillIfSanitizedEdgeFunction; +using psr::XTaint::makeComposeEF; +using psr::XTaint::makeFF; +using psr::XTaint::Sanitized; +using psr::XTaint::TransferEdgeFunction; +} // namespace psr::XTaint + +namespace psr::glca { +using psr::glca::BinaryEdgeFunction; +using psr::glca::compare; +using psr::glca::EdgeValue; +using psr::glca::EdgeValueSet; +using psr::glca::isConstant; +using psr::glca::join; +using psr::glca::Ordering; +using psr::glca::performBinOp; +using psr::glca::performTypecast; +using psr::glca::operator<; +using psr::glca::isTopValue; +using psr::glca::operator<<; +using psr::glca::EdgeValueSet; +using psr::glca::LCAEdgeFunctionComposer; +using psr::glca::MapFactsToCalleeFlowFunction; +using psr::glca::MapFactsToCallerFlowFunction; +using psr::glca::TypecastEdgeFunction; +using psr::glca::operator==; +using psr::glca::operator<<; +} // namespace psr::glca diff --git a/lib/PhasarLLVM/DataFlow/Mono/CMakeLists.txt b/lib/PhasarLLVM/DataFlow/Mono/CMakeLists.txt index 4bbda61731..d7e8cab632 100644 --- a/lib/PhasarLLVM/DataFlow/Mono/CMakeLists.txt +++ b/lib/PhasarLLVM/DataFlow/Mono/CMakeLists.txt @@ -1,6 +1,6 @@ file(GLOB_RECURSE MONO_SRC *.h *.cpp) -add_phasar_library(phasar_mono +add_phasar_library(phasar_llvm_mono ${MONO_SRC} LINKS @@ -13,4 +13,7 @@ add_phasar_library(phasar_mono LLVM_LINK_COMPONENTS Core Support + + MODULE_FILES + Mono.cppm ) diff --git a/lib/PhasarLLVM/DataFlow/Mono/Mono.cppm b/lib/PhasarLLVM/DataFlow/Mono/Mono.cppm new file mode 100644 index 0000000000..7b104ef449 --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/Mono/Mono.cppm @@ -0,0 +1,32 @@ +module; + +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h" + +export module phasar.llvm.dataflow.mono; + +export namespace psr { +using psr::DIBasedTypeHierarchy; +using psr::DToString; +using psr::InterMonoFullConstantPropagation; +using psr::InterMonoSolverTest; +using psr::InterMonoSolverTestDomain; +using psr::InterMonoTaintAnalysis; +using psr::InterMonoTaintAnalysisDomain; +using psr::IntraMonoFCAFact; +using psr::IntraMonoFullConstantPropagation; +using psr::IntraMonoFullConstantPropagationAnalysisDomain; +using psr::IntraMonoSolverTest; +using psr::IntraMonoSolverTestAnalysisDomain; +using psr::IntraMonoUninitVariables; +using psr::IntraMonoUninitVariablesDomain; +using psr::LLVMBasedCFG; +using psr::LLVMBasedICFG; +} // namespace psr + +namespace std { +using std::hash; +} // namespace std diff --git a/lib/PhasarLLVM/DataFlow/PathSensitivity/CMakeLists.txt b/lib/PhasarLLVM/DataFlow/PathSensitivity/CMakeLists.txt index e0d554e718..877cd983a7 100644 --- a/lib/PhasarLLVM/DataFlow/PathSensitivity/CMakeLists.txt +++ b/lib/PhasarLLVM/DataFlow/PathSensitivity/CMakeLists.txt @@ -41,4 +41,7 @@ add_phasar_library(phasar_llvm_pathsensitivity LINK_PUBLIC nlohmann_json::nlohmann_json ${ADDITIONAL_LINK_LIBS} + + MODULE_FILES + PathSensitivity.cppm ) diff --git a/lib/PhasarLLVM/DataFlow/PathSensitivity/PathSensitivity.cppm b/lib/PhasarLLVM/DataFlow/PathSensitivity/PathSensitivity.cppm new file mode 100644 index 0000000000..39a683287e --- /dev/null +++ b/lib/PhasarLLVM/DataFlow/PathSensitivity/PathSensitivity.cppm @@ -0,0 +1,14 @@ +module; + +#include "phasar/PhasarLLVM/DataFlow/PathSensitivity/LLVMPathConstraints.h" +#include "phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitivityConfig.h" +#include "phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h" + +export module phasar.llvm.dataflow.pathsensitivity; + +export namespace psr { +using psr::LLVMPathConstraints; +using psr::Z3BasedPathSensitivityConfig; +using psr::Z3BasedPathSensitivityManager; +using psr::Z3BasedPathSensitivityManagerBase; +} // namespace psr diff --git a/lib/PhasarLLVM/Domain/CMakeLists.txt b/lib/PhasarLLVM/Domain/CMakeLists.txt new file mode 100644 index 0000000000..e7dd25cf52 --- /dev/null +++ b/lib/PhasarLLVM/Domain/CMakeLists.txt @@ -0,0 +1,8 @@ +file(GLOB_RECURSE PSR_LLVM_DOMAIN_SRC *.h *.cpp) + +add_phasar_library(phasar_llvm_domain + ${PSR_LLVM_DOMAIN_SRC} + + MODULE_FILES + Domain.cppm +) diff --git a/lib/PhasarLLVM/Domain/Domain.cppm b/lib/PhasarLLVM/Domain/Domain.cppm new file mode 100644 index 0000000000..251de12cdf --- /dev/null +++ b/lib/PhasarLLVM/Domain/Domain.cppm @@ -0,0 +1,11 @@ +module; + +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" + +export module phasar.llvm.domain; + +export namespace psr { +using psr::LLVMAnalysisDomainDefault; +using LLVMIFDSAnalysisDomainDefault = + WithBinaryValueDomain; +} // namespace psr diff --git a/lib/PhasarLLVM/Passes/CMakeLists.txt b/lib/PhasarLLVM/Passes/CMakeLists.txt index d7b8613ba4..6349833195 100644 --- a/lib/PhasarLLVM/Passes/CMakeLists.txt +++ b/lib/PhasarLLVM/Passes/CMakeLists.txt @@ -11,4 +11,7 @@ add_phasar_library(phasar_passes Support Analysis Demangle + + MODULE_FILES + Passes.cppm ) diff --git a/lib/PhasarLLVM/Passes/Passes.cppm b/lib/PhasarLLVM/Passes/Passes.cppm new file mode 100644 index 0000000000..5dba1c68bf --- /dev/null +++ b/lib/PhasarLLVM/Passes/Passes.cppm @@ -0,0 +1,15 @@ +module; + +#include "phasar/PhasarLLVM/Passes/ExampleModulePass.h" +#include "phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h" +#include "phasar/PhasarLLVM/Passes/ValueAnnotationPass.h" + +export module phasar.llvm.passes; + +export namespace psr { +using psr::ExampleModulePass; +using psr::GeneralStatistics; +using psr::GeneralStatisticsAnalysis; +using psr::ValueAnnotationPass; +using psr::operator<<; +} // namespace psr diff --git a/lib/PhasarLLVM/PhasarLLVM.cppm b/lib/PhasarLLVM/PhasarLLVM.cppm new file mode 100644 index 0000000000..20e7a71a94 --- /dev/null +++ b/lib/PhasarLLVM/PhasarLLVM.cppm @@ -0,0 +1,21 @@ +module; + +#include "phasar/PhasarLLVM/HelperAnalyses.h" +#include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" + +export module phasar.llvm; + +export import phasar.llvm.controlflow; +export import phasar.llvm.dataflow; +export import phasar.llvm.db; +export import phasar.llvm.domain; +export import phasar.llvm.passes; +export import phasar.llvm.pointer; +export import phasar.llvm.taintconfig; +export import phasar.llvm.typehierarchy; +export import phasar.llvm.utils; + +export namespace psr { +using psr::createAnalysisProblem; +using psr::HelperAnalyses; +} // namespace psr diff --git a/lib/PhasarLLVM/Pointer/CMakeLists.txt b/lib/PhasarLLVM/Pointer/CMakeLists.txt index 522fd55e5e..f863a86046 100644 --- a/lib/PhasarLLVM/Pointer/CMakeLists.txt +++ b/lib/PhasarLLVM/Pointer/CMakeLists.txt @@ -18,6 +18,9 @@ add_phasar_library(phasar_llvm_pointer LINK_PRIVATE ${Boost_LIBRARIES} + + MODULE_FILES + Pointer.cppm ) add_subdirectory(external) diff --git a/lib/PhasarLLVM/Pointer/Pointer.cppm b/lib/PhasarLLVM/Pointer/Pointer.cppm new file mode 100644 index 0000000000..d7c812df74 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/Pointer.cppm @@ -0,0 +1,36 @@ +module; + +#include "phasar/Config/phasar-config.h" +#include "phasar/PhasarLLVM/Pointer/AliasAnalysisView.h" +#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" +#include "phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h" + +#ifdef PHASAR_USE_SVF +#include "phasar/PhasarLLVM/Pointer/SVF/SVFPointsToSet.h" +#endif + +export module phasar.llvm.pointer; + +export namespace psr { +using psr::AliasAnalysisView; +using psr::AliasInfoTraits; +using psr::FilteredLLVMAliasSet; +using psr::FunctionAliasView; +using psr::isInterestingPointer; +using psr::LLVMAliasInfo; +using psr::LLVMAliasInfoRef; +using psr::LLVMAliasSet; +using psr::LLVMAliasSetData; + +#ifdef PHASAR_USE_SVF +using psr::createSVFDDAPointsToInfo; +using psr::createSVFVFSPointsToInfo; +using psr::SVFBasedPointsToInfo; +using psr::SVFBasedPointsToInfoRef; +using psr::SVFPointsToInfoTraits; +#endif + +} // namespace psr diff --git a/lib/PhasarLLVM/TaintConfig/CMakeLists.txt b/lib/PhasarLLVM/TaintConfig/CMakeLists.txt index e3a9050f3e..09f24c3132 100644 --- a/lib/PhasarLLVM/TaintConfig/CMakeLists.txt +++ b/lib/PhasarLLVM/TaintConfig/CMakeLists.txt @@ -15,4 +15,7 @@ add_phasar_library(phasar_taintconfig LLVM_LINK_COMPONENTS Core Support + + MODULE_FILES + TaintConfig.cppm ) diff --git a/lib/PhasarLLVM/TaintConfig/TaintConfig.cppm b/lib/PhasarLLVM/TaintConfig/TaintConfig.cppm new file mode 100644 index 0000000000..0a57589687 --- /dev/null +++ b/lib/PhasarLLVM/TaintConfig/TaintConfig.cppm @@ -0,0 +1,23 @@ +module; + +#include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h" +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigData.h" +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h" + +export module phasar.llvm.taintconfig; + +export namespace psr { +using psr::collectGeneratedFacts; +using psr::collectLeakedFacts; +using psr::collectSanitizedFacts; +using psr::FunctionData; +using psr::LLVMTaintConfig; +using psr::parseTaintConfig; +using psr::parseTaintConfigOrNull; +using psr::TaintCategory; +using psr::TaintConfigBase; +using psr::TaintConfigData; +using psr::TaintConfigTraits; +using psr::VariableData; +} // namespace psr diff --git a/lib/PhasarLLVM/TypeHierarchy/CMakeLists.txt b/lib/PhasarLLVM/TypeHierarchy/CMakeLists.txt index d717d39f59..2f9d48f4b6 100644 --- a/lib/PhasarLLVM/TypeHierarchy/CMakeLists.txt +++ b/lib/PhasarLLVM/TypeHierarchy/CMakeLists.txt @@ -16,4 +16,7 @@ add_phasar_library(phasar_llvm_typehierarchy LINK_PRIVATE ${Boost_LIBRARIES} + + MODULE_FILES + TypeHierarchy.cppm ) diff --git a/lib/PhasarLLVM/TypeHierarchy/TypeHierarchy.cppm b/lib/PhasarLLVM/TypeHierarchy/TypeHierarchy.cppm new file mode 100644 index 0000000000..073668c6a3 --- /dev/null +++ b/lib/PhasarLLVM/TypeHierarchy/TypeHierarchy.cppm @@ -0,0 +1,19 @@ +module; + +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" + +export module phasar.llvm.typehierarchy; + +export namespace psr { +using psr::DIBasedTypeHierarchy; +using psr::DIBasedTypeHierarchyData; +using psr::LLVMProjectIRDB; +using psr::LLVMTypeHierarchyData; +using psr::LLVMVFTable; +using psr::LLVMVFTableData; +} // namespace psr diff --git a/lib/PhasarLLVM/Utils/CMakeLists.txt b/lib/PhasarLLVM/Utils/CMakeLists.txt index b88ee9d75b..e3def7c7f9 100644 --- a/lib/PhasarLLVM/Utils/CMakeLists.txt +++ b/lib/PhasarLLVM/Utils/CMakeLists.txt @@ -12,4 +12,7 @@ add_phasar_library(phasar_llvm_utils Support BitWriter Demangle + + MODULE_FILES + Utils.cppm ) diff --git a/lib/PhasarLLVM/Utils/Utils.cppm b/lib/PhasarLLVM/Utils/Utils.cppm new file mode 100644 index 0000000000..44043bc45d --- /dev/null +++ b/lib/PhasarLLVM/Utils/Utils.cppm @@ -0,0 +1,76 @@ +module; + +#include "phasar/PhasarLLVM/Utils/Annotation.h" +#include "phasar/PhasarLLVM/Utils/BasicBlockOrdering.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" +#include "phasar/PhasarLLVM/Utils/LLVMCXXShorthands.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/PhasarLLVM/Utils/LLVMSourceManager.h" +#include "phasar/PhasarLLVM/Utils/SourceMgrPrinter.h" + +export module phasar.llvm.utils; + +export namespace psr { +using psr::DataFlowAnalysisType; +using psr::DefaultDominatorTreeAnalysis; +using psr::GlobalAnnotation; +using psr::toDataFlowAnalysisType; +using psr::toString; +using psr::VarAnnotation; +using psr::operator<<; +using psr::appendAllExitPoints; +using psr::computeModuleHash; +using psr::DebugLocation; +using psr::dumpIRValue; +using psr::from_json; +using psr::getAllExitPoints; +using psr::getColumnFromIR; +using psr::getDebugLocation; +using psr::getDIFileFromIR; +using psr::getDILocalVariable; +using psr::getDILocation; +using psr::getDirectoryFromIR; +using psr::getFilePathFromIR; +using psr::getFunctionArgumentNr; +using psr::getFunctionNameFromIR; +using psr::getLastInstructionOf; +using psr::getLineAndColFromIR; +using psr::getLineFromIR; +using psr::getMetaDataID; +using psr::getModuleFromVal; +using psr::getModuleIDFromIR; +using psr::getModuleNameFromVal; +using psr::getModuleSlotTrackerFor; +using psr::getNthFunctionArgument; +using psr::getNthInstruction; +using psr::getNthStoreInstruction; +using psr::getNthTermInstruction; +using psr::getSrcCodeFromIR; +using psr::getSrcCodeInfoFromIR; +using psr::getVarAnnotationIntrinsicName; +using psr::getVarNameFromIR; +using psr::getVarTypeFromIR; +using psr::globalValuesUsedinFunction; +using psr::isAllocaInstOrHeapAllocaFunction; +using psr::isGuardVariable; +using psr::isHeapAllocatingFunction; +using psr::isIntegerLikeType; +using psr::isStaticVariableLazyInitializationBranch; +using psr::isTouchVTableInst; +using psr::isVarAnnotationIntrinsic; +using psr::llvmIRToShortString; +using psr::llvmIRToStableString; +using psr::llvmIRToString; +using psr::LLVMSourceManager; +using psr::llvmTypeToString; +using psr::LLVMValueIDLess; +using psr::ManagedDebugLocation; +using psr::matchesSignature; +using psr::ModulesToSlotTracker; +using psr::Ref2PointerConverter; +using psr::SourceCodeInfo; +using psr::SourceMgrPrinter; +using psr::to_json; +} // namespace psr diff --git a/lib/PhasarPass/CMakeLists.txt b/lib/PhasarPass/CMakeLists.txt index dd4dff3cb4..19e02c7084 100644 --- a/lib/PhasarPass/CMakeLists.txt +++ b/lib/PhasarPass/CMakeLists.txt @@ -11,7 +11,7 @@ add_phasar_library(phasar_pass phasar_llvm_controlflow phasar_llvm_db phasar_llvm_ifdside - phasar_mono + phasar_llvm_mono phasar_passes phasar_llvm_utils phasar_llvm_pointer @@ -25,4 +25,7 @@ add_phasar_library(phasar_pass LINK_PUBLIC ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} + + MODULE_FILES + PhasarPass.cppm ) diff --git a/lib/PhasarPass/PhasarPass.cppm b/lib/PhasarPass/PhasarPass.cppm new file mode 100644 index 0000000000..b4dfd79f81 --- /dev/null +++ b/lib/PhasarPass/PhasarPass.cppm @@ -0,0 +1,20 @@ +module; + +#include "phasar/PhasarPass/Options.h" +#include "phasar/PhasarPass/PhasarPass.h" +#include "phasar/PhasarPass/PhasarPrinterPass.h" + +export module phasar.phasarpass; + +export namespace psr { +using psr::CallGraphAnalysis; +using psr::DataFlowAnalysis; +using psr::DumpResults; +using psr::EntryPoints; +using psr::InitLogger; +using psr::PammOutputFile; +using psr::PhasarPass; +using psr::PhasarPrinterPass; +using psr::PointerAnalysis; +using psr::PrintEdgeRecorder; +} // namespace psr diff --git a/lib/Pointer/CMakeLists.txt b/lib/Pointer/CMakeLists.txt index 4695aeb0ae..66b1d2710f 100644 --- a/lib/Pointer/CMakeLists.txt +++ b/lib/Pointer/CMakeLists.txt @@ -9,4 +9,7 @@ add_phasar_library(phasar_pointer LLVM_LINK_COMPONENTS Support + + MODULE_FILES + PhasarPointer.cppm ) diff --git a/lib/Pointer/PhasarPointer.cppm b/lib/Pointer/PhasarPointer.cppm new file mode 100644 index 0000000000..21e84a075a --- /dev/null +++ b/lib/Pointer/PhasarPointer.cppm @@ -0,0 +1,38 @@ +module; + +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasInfo.h" +#include "phasar/Pointer/AliasInfoBase.h" +#include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Pointer/AliasSetOwner.h" +#include "phasar/Pointer/PointsToInfo.h" + +export module phasar.pointer; + +export namespace psr { +using psr::AliasAnalysisType; +using psr::toAliasAnalysisType; +using psr::toString; +using psr::operator<<; +using psr::AliasAnalysisType; +using psr::AliasInfo; +using psr::AliasInfoBaseUtils; +using psr::AliasInfoRef; +using psr::AliasInfoTraits; +using psr::AliasResult; +using psr::AnalysisProperties; +using psr::DefaultAATraits; +using psr::IsAliasInfo; +using psr::toAliasResult; +using psr::toString; +using psr::operator<<; +using psr::AliasSetOwner; +using psr::is_equivalent_PointsToTraits_v; +using psr::is_PointsToTraits; +using psr::is_PointsToTraits_v; +using psr::PointsToInfo; +using psr::PointsToInfoBase; +using psr::PointsToInfoRef; +using psr::PointsToTraits; +} // namespace psr diff --git a/lib/TypeHierarchy/CMakeLists.txt b/lib/TypeHierarchy/CMakeLists.txt new file mode 100644 index 0000000000..734cb7c9d7 --- /dev/null +++ b/lib/TypeHierarchy/CMakeLists.txt @@ -0,0 +1,9 @@ +file(GLOB_RECURSE TYPEHIERARCHY *.h *.cpp) + +# Handle the library files +add_phasar_library(phasar_typehierarchy + ${TYPEHIERARCHY} + + MODULE_FILES + TypeHierarchy.cppm +) diff --git a/lib/TypeHierarchy/TypeHierarchy.cppm b/lib/TypeHierarchy/TypeHierarchy.cppm new file mode 100644 index 0000000000..a7bcbd7ccd --- /dev/null +++ b/lib/TypeHierarchy/TypeHierarchy.cppm @@ -0,0 +1,12 @@ +module; + +#include "phasar/TypeHierarchy/TypeHierarchy.h" +#include "phasar/TypeHierarchy/VFTable.h" + +export module phasar.typehierarchy; + +export namespace psr { +using psr::TypeHierarchy; +using psr::operator<<; +using psr::VFTable; +} // namespace psr diff --git a/lib/Utils/CMakeLists.txt b/lib/Utils/CMakeLists.txt index c105696638..2bee013212 100644 --- a/lib/Utils/CMakeLists.txt +++ b/lib/Utils/CMakeLists.txt @@ -19,6 +19,9 @@ add_phasar_library(phasar_utils ${PHASAR_STD_FILESYSTEM} LINK_PUBLIC nlohmann_json::nlohmann_json + + MODULE_FILES + Utils.cppm ) set_target_properties(phasar_utils diff --git a/lib/Utils/Utils.cppm b/lib/Utils/Utils.cppm new file mode 100644 index 0000000000..9440d6c5dd --- /dev/null +++ b/lib/Utils/Utils.cppm @@ -0,0 +1,199 @@ +module; + +#include "phasar/Utils/AdjacencyList.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/AnalysisProperties.h" +#include "phasar/Utils/Average.h" +#include "phasar/Utils/BitVectorSet.h" +#include "phasar/Utils/BoxedPointer.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/ChronoUtils.h" +#include "phasar/Utils/DFAMinimizer.h" +#include "phasar/Utils/DOTGraph.h" +#include "phasar/Utils/DebugOutput.h" +#include "phasar/Utils/DefaultAnalysisPrinter.h" +#include "phasar/Utils/DefaultValue.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" +#include "phasar/Utils/EnumFlags.h" +#include "phasar/Utils/EquivalenceClassMap.h" +#include "phasar/Utils/ErrorHandling.h" +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/InitPhasar.h" +#include "phasar/Utils/IotaIterator.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/NullAnalysisPrinter.h" +#include "phasar/Utils/Nullable.h" +#include "phasar/Utils/OnTheFlyAnalysisPrinter.h" +#include "phasar/Utils/PAMM.h" +#include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/PointerUtils.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/RepeatIterator.h" +#include "phasar/Utils/SemiRing.h" +#include "phasar/Utils/Soundness.h" +#include "phasar/Utils/StableVector.h" +#include "phasar/Utils/Table.h" +#include "phasar/Utils/TableWrappers.h" +#include "phasar/Utils/Timer.h" +#include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" + +export module phasar.utils; + +export namespace psr { +using psr::AdjacencyList; +using psr::AnalysisPrinterBase; +using psr::AnalysisProperties; +using psr::GraphTraits; +using psr::to_string; +using psr::operator<<; +using psr::AnalysisPropertiesMixin; +using psr::BitVectorSet; +using psr::BoxedConstPtr; +using psr::BoxedPtr; +using psr::CanEfficientlyPassByValue; +using psr::hms; +using psr::intersectWith; +using psr::PrettyPrinter; +using psr::Sampler; +using psr::operator<<; +using psr::createEquivalentGraphFrom; +using psr::DefaultAnalysisPrinter; +using psr::DOTConfig; +using psr::DOTEdge; +using psr::DOTNode; +using psr::getDefaultValue; +using psr::minimizeGraph; +using psr::Warning; +using psr::operator<; +using psr::operator==; +using psr::DOTFactSubGraph; +using psr::DOTFunctionSubGraph; +using psr::DOTGraph; +using psr::DummyPair; +using psr::EmptyType; +using psr::EnumFlagAutoBool; +using psr::operator&=; +using psr::operator|; +using psr::operator|=; +using psr::operator^; +using psr::operator^=; +using psr::operator~; +using psr::DefaultNodeTransform; +using psr::EquivalenceClassMap; +using psr::EquivalenceClassMapNG; +using psr::getOrEmpty; +using psr::getOrNull; +using psr::getOrThrow; +using psr::GraphTraits; +using psr::hasFlag; +using psr::InitPhasar; +using psr::iota; +using psr::IotaIterator; +using psr::is_graph; +using psr::is_graph_edge; +using psr::is_graph_trait; +using psr::is_removable_graph_trait_v; +using psr::is_reservable_graph_trait_v; +using psr::JoinLattice; +using psr::JoinLatticeTraits; +using psr::Logger; +using psr::mapValue; +using psr::MaybeUniquePtr; +using psr::NonTopBotValue; +using psr::openFileStream; +using psr::parseSeverityLevel; +using psr::printGraph; +using psr::readFile; +using psr::readFileOrErr; +using psr::readFileOrNull; +using psr::readJsonFile; +using psr::readTextFile; +using psr::readTextFileOrErr; +using psr::readTextFileOrNull; +using psr::reverseGraph; +using psr::setFlag; +using psr::SeverityLevel; +using psr::to_string; +using psr::unsetFlag; +using psr::writeTextFile; +using psr::operator<<; +using psr::DToString; +using psr::FToString; +using psr::getPointerFrom; +using psr::LToString; +using psr::NToString; +using psr::NullAnalysisPrinter; +using psr::OnTheFlyAnalysisPrinter; +using psr::PAMM; +using psr::PAMM_SEVERITY_LEVEL; +using psr::repeat; +using psr::RepeatIterator; +using psr::SemiRing; +using psr::Soundness; +using psr::toSoundness; +using psr::toString; +using psr::unwrapNullable; +using psr::operator<<; +using psr::adl_to_string; +using psr::AreEqualityComparable; +using psr::assertAllNotNull; +using psr::assertNotNull; +using psr::computePowerSet; +using psr::createTimeStamp; +using psr::DefaultConstruct; +using psr::DenseSet; +using psr::DenseTable1d; +using psr::DummyDenseTable1d; +using psr::DummyUnorderedTable1d; +using psr::ElementType; +using psr::FalseFn; +using psr::forward_like; +using psr::has_adl_to_string_v; +using psr::has_erase_iterator_v; +using psr::has_getAsJson; +using psr::has_getHashCode; +using psr::has_isInteresting_v; +using psr::has_llvm_dense_map_info; +using psr::has_std_hash; +using psr::has_str_v; +using psr::HasDepth; +using psr::HasIsConstant; +using psr::identity; +using psr::IdentityFn; +using psr::IgnoreArgs; +using psr::intersectWith; +using psr::is_crtp_base_of_v; +using psr::is_iterable_over_v; +using psr::is_iterable_v; +using psr::is_llvm_hashable_v; +using psr::is_llvm_printable_v; +using psr::is_pair_v; +using psr::is_printable_v; +using psr::is_std_hashable_v; +using psr::is_std_printable_v; +using psr::is_string_like_v; +using psr::is_tuple_v; +using psr::is_variant; +using psr::is_variant_v; +using psr::isConstructor; +using psr::IsEqualityComparable; +using psr::isMangled; +using psr::Overloaded; +using psr::remove_by_index; +using psr::reserveIfPossible; +using psr::scope_exit; +using psr::SmallDenseTable1d; +using psr::StableVector; +using psr::StringIDLess; +using psr::Table; +using psr::Timer; +using psr::TrueFn; +using psr::UnorderedSet; +using psr::UnorderedTable1d; +using psr::variant_idx; +// using psr::variant_idx; +} // namespace psr diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 64a97d80a7..a1b50268ee 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,2 +1,5 @@ add_subdirectory(example-tool) add_subdirectory(phasar-cli) +if (PHASAR_BUILD_MODULES) + add_subdirectory(hello-modules-tool) +endif() diff --git a/tools/hello-modules-tool/CMakeLists.txt b/tools/hello-modules-tool/CMakeLists.txt new file mode 100644 index 0000000000..f38f50030e --- /dev/null +++ b/tools/hello-modules-tool/CMakeLists.txt @@ -0,0 +1,18 @@ +# Build a stand-alone executable +if(PHASAR_IN_TREE) + # Build a small test tool to show how phasar may be used + add_phasar_executable(hello-modules + hello_modules.cpp + ) +else() + # Build a small test tool to show how phasar may be used + add_executable(hello-modules + hello_modules.cpp + ) +endif() + +target_link_libraries(hello-modules + PRIVATE + phasar + ${PHASAR_STD_FILESYSTEM} +) diff --git a/tools/hello-modules-tool/hello_modules.cpp b/tools/hello-modules-tool/hello_modules.cpp new file mode 100644 index 0000000000..edccb798f0 --- /dev/null +++ b/tools/hello-modules-tool/hello_modules.cpp @@ -0,0 +1,65 @@ +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +import phasar.analysisstrategy; +import phasar.config; +import phasar.controlflow; +import phasar.dataflow; +import phasar.db; +import phasar.domain; +import phasar.llvm; +import phasar.phasarpass; +import phasar.pointer; +import phasar.typehierarchy; +import phasar.utils; + +using namespace psr; + +int main(int Argc, const char **Argv) { + using namespace std::string_literals; + + if (Argc < 2 || !std::filesystem::exists(Argv[1]) || + std::filesystem::is_directory(Argv[1])) { + llvm::errs() << "myphasartool\n" + "A small PhASAR-based example program\n\n" + "Usage: myphasartool \n"; + return 1; + } + + std::vector EntryPoints = {"main"s}; + + HelperAnalyses HA(Argv[1], EntryPoints); + if (!HA.getProjectIRDB().isValid()) { + return 1; + } + + if (const auto *F = HA.getProjectIRDB().getFunctionDefinition("main")) { + // print type hierarchy + HA.getTypeHierarchy().print(); + // print points-to information + HA.getAliasInfo().print(); + // print inter-procedural control-flow graph + HA.getICFG().print(); + + // IFDS template parametrization test + llvm::outs() << "Testing IFDS:\n"; + auto L = createAnalysisProblem(HA, EntryPoints); + IFDSSolver S(L, &HA.getICFG()); + auto IFDSResults = S.solve(); + IFDSResults.dumpResults(HA.getICFG()); + + // IDE template parametrization test + llvm::outs() << "Testing IDE:\n"; + auto M = createAnalysisProblem(HA, EntryPoints); + // Alternative way of solving an IFDS/IDEProblem: + auto IDEResults = solveIDEProblem(M, HA.getICFG()); + IDEResults.dumpResults(HA.getICFG()); + + } else { + llvm::errs() << "error: file does not contain a 'main' function!\n"; + } + return 0; +} From e3908bd34d66317ff60003551e99ad9655a1b0d0 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 30 Jun 2025 17:49:38 +0200 Subject: [PATCH 12/24] Example Programs (#774) * Update llvm-hello-world + add examples for load-llvm-ir and build-type-hierarchy with phasar * Add examples on how to build a call-graph * Update CG examples to include the reverse-CG + add example for alias info * Add taint analysis example * Add IDE linear constant analysis example * Build and run example programs in CI * Fixing small isuues * Fix examples path in CI * Install phasar to custom location in CI * Set env for install and examples-build in CI * Next attempt to fix CI * Adding simple example on how to write an IFDS analysis * Improve TaintConfigUtilities to provide API that *returns* affected facts, instead of inserting them into an already-existing set * minor * Small simplification * Add example on how to write an IDE linear constant analysis based on the corresponding Wiki entry * Update readmes * Fix broken compile due to merge * minor restructure in README.md * cleanup readme --- .github/workflows/ci.yml | 15 +- BUILD.md | 130 +++ README.md | 167 +-- .../how-to/00-load-llvm-ir/CMakeLists.txt | 19 + examples/how-to/00-load-llvm-ir/README.md | 24 + examples/how-to/00-load-llvm-ir/main.cpp | 55 + .../01-build-type-hierarchy/CMakeLists.txt | 19 + .../how-to/01-build-type-hierarchy/README.md | 24 + .../how-to/01-build-type-hierarchy/main.cpp | 47 + .../how-to/02-build-call-graph/CMakeLists.txt | 23 + examples/how-to/02-build-call-graph/README.md | 38 + .../build_llvm_based_call_graph.cpp | 87 ++ .../build_llvm_based_icfg.cpp | 81 ++ .../how-to/02-build-call-graph/img/cg.svg | 298 +++++ .../03-create-alias-info/CMakeLists.txt | 19 + .../how-to/03-create-alias-info/README.md | 24 + examples/how-to/03-create-alias-info/main.cpp | 84 ++ .../04-run-ifds-analysis/CMakeLists.txt | 31 + .../how-to/04-run-ifds-analysis/README.md | 34 + .../04-run-ifds-analysis/helper-analyses.cpp | 52 + .../04-run-ifds-analysis/ifds-solver.cpp | 73 ++ .../04-run-ifds-analysis/otf-reporter.cpp | 65 ++ .../how-to/04-run-ifds-analysis/simple.cpp | 71 ++ .../how-to/05-run-ide-analysis/CMakeLists.txt | 27 + examples/how-to/05-run-ide-analysis/README.md | 32 + .../05-run-ide-analysis/helper-analyses.cpp | 65 ++ .../how-to/05-run-ide-analysis/ide-solver.cpp | 73 ++ .../how-to/05-run-ide-analysis/simple.cpp | 59 + .../07-write-ifds-analysis/CMakeLists.txt | 20 + .../how-to/07-write-ifds-analysis/README.md | 28 + .../how-to/07-write-ifds-analysis/simple.cpp | 146 +++ .../08-write-ide-analysis/CMakeLists.txt | 20 + .../how-to/08-write-ide-analysis/README.md | 29 + .../how-to/08-write-ide-analysis/simple.cpp | 484 ++++++++ examples/how-to/CMakeLists.txt | 13 + examples/how-to/README.md | 13 + examples/llvm-hello-world/CMakeLists.txt | 13 + examples/llvm-hello-world/README.md | 30 + examples/llvm-hello-world/README.txt | 12 - examples/llvm-hello-world/main.cpp | 4 - .../llvm-hello-world/target/CMakeLists.txt | 12 + examples/llvm-hello-world/target/branching.ll | 116 +- .../llvm-hello-world/target/branching2.ll | 122 +- examples/llvm-hello-world/target/call.ll | 66 +- examples/llvm-hello-world/target/call2.ll | 170 ++- .../target/class_hierarchy.cpp | 33 + .../target/class_hierarchy.ll | 596 ++++++++++ examples/llvm-hello-world/target/exception.ll | 1012 +++++++++++++++-- examples/llvm-hello-world/target/functions.ll | 157 ++- .../llvm-hello-world/target/functions2.ll | 433 ++++--- .../target/llvm_intrinsic_functions.ll | 170 ++- .../target/llvm_intrinsic_functions2.ll | 435 ++++++- examples/llvm-hello-world/target/loop.ll | 136 ++- examples/llvm-hello-world/target/loop2.ll | 99 +- examples/llvm-hello-world/target/pointers.ll | 101 +- examples/llvm-hello-world/target/pointers2.ll | 363 +++++- examples/llvm-hello-world/target/simple.ll | 72 +- examples/llvm-hello-world/target/simple2.ll | 120 +- examples/llvm-hello-world/target/struct.ll | 91 +- examples/llvm-hello-world/target/struct2.ll | 146 ++- examples/llvm-hello-world/target/struct3.ll | 223 ++-- examples/llvm-hello-world/target/taint.cpp | 35 + examples/llvm-hello-world/target/taint.ll | 673 +++++++++++ examples/use-phasar-as-library/README.md | 1 + .../use-phasar-with-fetch-content/README.md | 2 + .../phasar/DataFlow/IfdsIde/EntryPointUtils.h | 43 +- .../phasar/DataFlow/IfdsIde/InitialSeeds.h | 4 + include/phasar/PhasarLLVM/DataFlow.h | 2 + .../IfdsIde/DefaultAliasAwareIDEProblem.h | 4 +- .../TaintConfig/TaintConfigUtilities.h | 47 + .../TaintConfig/LLVMTaintConfig.cpp | 2 +- phasar-clang_more_help.txt | 11 - phasar-llvm_more_help.txt | 32 - 73 files changed, 6971 insertions(+), 1106 deletions(-) create mode 100644 BUILD.md create mode 100644 examples/how-to/00-load-llvm-ir/CMakeLists.txt create mode 100644 examples/how-to/00-load-llvm-ir/README.md create mode 100644 examples/how-to/00-load-llvm-ir/main.cpp create mode 100644 examples/how-to/01-build-type-hierarchy/CMakeLists.txt create mode 100644 examples/how-to/01-build-type-hierarchy/README.md create mode 100644 examples/how-to/01-build-type-hierarchy/main.cpp create mode 100644 examples/how-to/02-build-call-graph/CMakeLists.txt create mode 100644 examples/how-to/02-build-call-graph/README.md create mode 100644 examples/how-to/02-build-call-graph/build_llvm_based_call_graph.cpp create mode 100644 examples/how-to/02-build-call-graph/build_llvm_based_icfg.cpp create mode 100644 examples/how-to/02-build-call-graph/img/cg.svg create mode 100644 examples/how-to/03-create-alias-info/CMakeLists.txt create mode 100644 examples/how-to/03-create-alias-info/README.md create mode 100644 examples/how-to/03-create-alias-info/main.cpp create mode 100644 examples/how-to/04-run-ifds-analysis/CMakeLists.txt create mode 100644 examples/how-to/04-run-ifds-analysis/README.md create mode 100644 examples/how-to/04-run-ifds-analysis/helper-analyses.cpp create mode 100644 examples/how-to/04-run-ifds-analysis/ifds-solver.cpp create mode 100644 examples/how-to/04-run-ifds-analysis/otf-reporter.cpp create mode 100644 examples/how-to/04-run-ifds-analysis/simple.cpp create mode 100644 examples/how-to/05-run-ide-analysis/CMakeLists.txt create mode 100644 examples/how-to/05-run-ide-analysis/README.md create mode 100644 examples/how-to/05-run-ide-analysis/helper-analyses.cpp create mode 100644 examples/how-to/05-run-ide-analysis/ide-solver.cpp create mode 100644 examples/how-to/05-run-ide-analysis/simple.cpp create mode 100644 examples/how-to/07-write-ifds-analysis/CMakeLists.txt create mode 100644 examples/how-to/07-write-ifds-analysis/README.md create mode 100644 examples/how-to/07-write-ifds-analysis/simple.cpp create mode 100644 examples/how-to/08-write-ide-analysis/CMakeLists.txt create mode 100644 examples/how-to/08-write-ide-analysis/README.md create mode 100644 examples/how-to/08-write-ide-analysis/simple.cpp create mode 100644 examples/how-to/CMakeLists.txt create mode 100644 examples/how-to/README.md create mode 100644 examples/llvm-hello-world/CMakeLists.txt create mode 100644 examples/llvm-hello-world/README.md delete mode 100644 examples/llvm-hello-world/README.txt create mode 100644 examples/llvm-hello-world/target/CMakeLists.txt create mode 100644 examples/llvm-hello-world/target/class_hierarchy.cpp create mode 100644 examples/llvm-hello-world/target/class_hierarchy.ll create mode 100644 examples/llvm-hello-world/target/taint.cpp create mode 100644 examples/llvm-hello-world/target/taint.ll delete mode 100644 phasar-clang_more_help.txt delete mode 100644 phasar-llvm_more_help.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a68d15bcdc..0031d9ef5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -63,5 +63,16 @@ jobs: - name: Run Unittests shell: bash run: | - cd build - cmake --build . --target check-phasar-unittests + cmake --build ./build --target check-phasar-unittests + + - name: Install PhASAR and Build Examples + if: matrix.build == 'DebugLibdeps' # Circumvent conflicting ASAn flags + env: + CXX: ${{ matrix.compiler[0] }} + CC: ${{ matrix.compiler[1] }} + shell: bash + run: | + cmake -DCMAKE_INSTALL_PREFIX=./INSTALL -P ./build/cmake_install.cmake + cd ./examples/how-to + cmake -S . -B build -Dphasar_ROOT=../../INSTALL + cmake --build ./build --target run_sample_programs diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000000..a98acf7cdb --- /dev/null +++ b/BUILD.md @@ -0,0 +1,130 @@ +# Building PhASAR + + +It is recommended to compile PhASAR yourself in order to get the full C++ experience and to have full control over the build mode. +However, you may also want to try out one of the pre-built versions of PhASAR or the Docker container. + +As a shortcut for the very first PhASAR build on your system, you can use our [bootstrap](./bootstrap.sh) script. +Please note that you must have python installed for the script to work properly. + +```bash +./bootstrap.sh +``` + +Note: If you want to do changes within PhASAR, it is recommended to build it in Debug mode: + +```bash +./bootstrap.sh -DCMAKE_BUILD_TYPE=Debug +``` + +The bootstrap script may ask for superuser permissions (to install the dependencies); however it is not recommended to start the whole script with `sudo`. + +For subsequent builds, see [Compiling PhASAR](#compiling-phasar-if-not-already-done-using-the-installation-scripts). + +### Compiling PhASAR (if not already done using the bootstrap script) + +Set the system's variables for the C and C++ compiler to clang: + +```bash +export CC=/usr/local/bin/clang +export CXX=/usr/local/bin/clang++ +``` + +You may need to adjust the paths according to your system. When you cloned PhASAR from Github you need to initialize PhASAR's submodules before building it: + +```bash +git submodule update --init +``` + +If you downloaded PhASAR as a compressed release (e.g. .zip or .tar.gz) you can use the `init-submodules-release.sh` script that manually clones the required submodules: + +```bash +utils/init-submodules-release.sh +``` + +Navigate into the PhASAR directory. The following commands will do the job and compile the PhASAR framework: + +```bash +mkdir build +cd build/ +cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .. +ninja -j $(nproc) # or use a different number of cores to compile it +sudo ninja install # only if you wish to install PhASAR system wide +``` + +When you have used the `bootstrap.sh` script to install PhASAR, the above steps are already done. +Use them as a reference if you wish to modify PhASAR and recompile it. + +After compilation using cmake the following two binaries can be found in the build/tools directory: + ++ `phasar-cli` - the PhASAR command-line tool (previously called `phasar-llvm`) that provides access to analyses that are already implemented within PhASAR. Use this if you don't want to build an own tool on top of PhASAR. ++ `myphasartool` - an example tool that shows how tools can be build on top of PhASAR + +Please be careful and check if errors occur during the compilation. + +When using CMake to compile PhASAR the following optional parameters can be used: + +| Parameter : Type| Effect | +|-----------|--------| +| **BUILD_SHARED_LIBS** : BOOL | Build shared libraries -- Not recommended anymore. You may want to use PHASAR_BUILD_DYNLIB instead (default is OFF) | +| **PHASAR_BUILD_DYNLIB** : BOOL | Build one fat shared library (default is OFF) | +| **CMAKE_BUILD_TYPE** : STRING | Build PhASAR in 'Debug', 'RelWithDebInfo' or 'Release' mode (default is 'Debug') | +| **CMAKE_INSTALL_PREFIX** : PATH | Path where PhASAR will be installed if "ninja install” is invoked or the “install” target is built (default is /usr/local/phasar) | +| **PHASAR_CUSTOM_CONFIG_INSTALL_DIR** : PATH | If set, customizes the directory, where configuration files for PhASAR are installed (default is /usr/local/.phasar-config)| +| **PHASAR_ENABLE_DYNAMIC_LOG** : BOOL|Makes it possible to switch the logger on and off at runtime (default is ON)| +| **PHASAR_BUILD_DOC** : BOOL | Build PhASAR documentation (default is OFF) | +| **PHASAR_BUILD_UNITTESTS** : BOOL | Build PhASAR unit tests (default is ON) | +| **PHASAR_BUILD_IR** : BOOL | Build PhASAR IR (required for running the unit tests) (default is ON) | +| **PHASAR_BUILD_OPENSSL_TS_UNITTESTS** : BOOL | Build PhASAR unit tests that require OpenSSL (default is OFF) | +| **PHASAR_ENABLE_PAMM** : STRING | Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is Off) | +| **PHASAR_ENABLE_PIC** : BOOL | Build Position-Independed Code (default is ON) | +| **PHASAR_ENABLE_WARNINGS** : BOOL | Enable compiler warnings (default is ON) | +| **CMAKE_CXX_STANDARD** : INT|Build phasar in C++17 or C++20 mode (default is 17)| + +You can use these parameters either directly or modify the installer-script `bootstrap.sh` + +#### A Remark on Compile Time + +C++'s long compile times are always a pain. As shown in the above, when using cmake the compilation can easily be run in parallel, resulting in shorter compilation times. Make use of it! + +### Running a Test Solver + +To test if everything works as expected please run the following command: + +`$ phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest` + +You can find the `phasar-cli` tool in the build-tree under `tools/phasar-cli`. + +If you obtain output other than a segmentation fault or an exception terminating the program abnormally everything works as expected. + +### Building PhASAR on a MacOS System + +Due to unfortunate updates to MacOS and the handling of C++, especially on the newer M1 processors, we can't support native development on Mac. +The easiest solution to develop PhASAR on a Mac right now is to use [dockers development environments](https://docs.docker.com/desktop/dev-environments/). Clone this repository as described in their documentation. Afterwards, you have to login once manually, as a root user by running `docker exec -it -u root /bin/bash` to complete the rest of the build process as described in this readme (install submodules, run bootstrap.sh, ...). +Now you can just attach your docker container to VS Code or any other IDE, which supports remote development. + +## Installation + +PhASAR can be installed using the installer scripts as explained in the following. +However, you do not need to install PhASAR in order to use it. + +### Installing PhASAR on an Ubuntu System + +In the following, we would like to give an complete example of how to install +PhASAR using an Ubuntu or Unix-like system. + +Therefore, we provide an installation script. To install PhASAR, just navigate to the top-level +directory of PhASAR and use the following command: + +```bash +./bootstrap.sh --install +``` + +The bootstrap script may ask for superuser permissions. + +Done! + +If You have already built phasar, you can just invoke +```bash +sudo ninja install +``` diff --git a/README.md b/README.md index d5a5538a7b..a6342ea32e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,33 @@ ![PhASAR logo](img/Logo_RGB/Phasar_Logo.png) -# PhASAR a LLVM-based Static Analysis Framework +# PhASAR: A LLVM-based Static Analysis Framework [![C++ Standard](https://img.shields.io/badge/C++_Standard-C%2B%2B17-blue.svg?style=flat&logo=c%2B%2B)](https://isocpp.org/) [![GitHub license](https://img.shields.io/badge/license-MIT-blueviolet.svg)](https://raw.githubusercontent.com/secure-software-engineering/phasar/master/LICENSE.txt) Version 2503 +## What is PhASAR? + +PhASAR is a LLVM-based static analysis framework written in C++. It allows users +to specify arbitrary data-flow problems which are then solved in a +fully-automated manner on the specified LLVM IR target code. Computing points-to +information, call-graph(s), etc. is done by the framework, thus you can focus on +what matters. + +You can find available literature on PhASAR [here](https://github.com/secure-software-engineering/phasar/wiki/Useful-Literature#papers-on-phasar). + +### How do I get started with PhASAR? + +We have some documentation on PhASAR in our [***Wiki***](https://github.com/secure-software-engineering/phasar/wiki). You probably would like to read +this README first. + +Please also have a look on PhASAR's project directory and notice the project directory [examples](./examples/) as well as the custom tool `tools/example-tool/myphasartool.cpp`. + +**NEW:** You can find PhASAR's API reference [here](https://secure-software-engineering.github.io/phasar/). + + ## Secure Software Engineering Group PhASAR is primarily developed and maintained by the Secure Software Engineering Group at Heinz Nixdorf Institute (University of Paderborn) and Fraunhofer IEM. @@ -33,155 +54,13 @@ Although phasar currently does not make use of C++-20 features (except for some PhASAR is currently set up to support LLVM-15.0.* -## What is PhASAR? - -PhASAR is a LLVM-based static analysis framework written in C++. It allows users -to specify arbitrary data-flow problems which are then solved in a -fully-automated manner on the specified LLVM IR target code. Computing points-to -information, call-graph(s), etc. is done by the framework, thus you can focus on -what matters. - ## Breaking Changes To keep PhASAR in a state that it is well suited for state-of-the-art research in static analysis, as well as for productive use, we have to make breaking changes. Please refer to [Breaking Changes](./BreakingChanges.md) for detailed information on what was broken recently and how to migrate. -## How do I get started with PhASAR? - -We have some documentation on PhASAR in our [***Wiki***](https://github.com/secure-software-engineering/phasar/wiki). You probably would like to read -this README first. - -Please also have a look on PhASAR's project directory and notice the project directory `examples/` as well as the custom tool `tools/example-tool/myphasartool.cpp`. - ## Building PhASAR -It is recommended to compile PhASAR yourself in order to get the full C++ experience and to have full control over the build mode. -However, you may also want to try out one of the pre-built versions of PhASAR or the Docker container. - -As a shortcut for the very first PhASAR build on your system, you can use our [bootstrap](./bootstrap.sh) script. -Please note that you must have python installed for the script to work properly. - -```bash -./bootstrap.sh -``` - -Note: If you want to do changes within PhASAR, it is recommended to build it in Debug mode: - -```bash -./bootstrap.sh -DCMAKE_BUILD_TYPE=Debug -``` - -The bootstrap script may ask for superuser permissions (to install the dependencies); however it is not recommended to start the whole script with `sudo`. - -For subsequent builds, see [Compiling PhASAR](#compiling-phasar-if-not-already-done-using-the-installation-scripts). - -### Compiling PhASAR (if not already done using the bootstrap script) - -Set the system's variables for the C and C++ compiler to clang: - -```bash -export CC=/usr/local/bin/clang -export CXX=/usr/local/bin/clang++ -``` - -You may need to adjust the paths according to your system. When you cloned PhASAR from Github you need to initialize PhASAR's submodules before building it: - -```bash -git submodule update --init -``` - -If you downloaded PhASAR as a compressed release (e.g. .zip or .tar.gz) you can use the `init-submodules-release.sh` script that manually clones the required submodules: - -```bash -utils/init-submodules-release.sh -``` - -Navigate into the PhASAR directory. The following commands will do the job and compile the PhASAR framework: - -```bash -mkdir build -cd build/ -cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .. -ninja -j $(nproc) # or use a different number of cores to compile it -sudo ninja install # only if you wish to install PhASAR system wide -``` - -When you have used the `bootstrap.sh` script to install PhASAR, the above steps are already done. -Use them as a reference if you wish to modify PhASAR and recompile it. - -After compilation using cmake the following two binaries can be found in the build/tools directory: - -+ `phasar-cli` - the PhASAR command-line tool (previously called `phasar-llvm`) that provides access to analyses that are already implemented within PhASAR. Use this if you don't want to build an own tool on top of PhASAR. -+ `myphasartool` - an example tool that shows how tools can be build on top of PhASAR - -Please be careful and check if errors occur during the compilation. - -When using CMake to compile PhASAR the following optional parameters can be used: - -| Parameter : Type| Effect | -|-----------|--------| -| **BUILD_SHARED_LIBS** : BOOL | Build shared libraries -- Not recommended anymore. You may want to use PHASAR_BUILD_DYNLIB instead (default is OFF) | -| **PHASAR_BUILD_DYNLIB** : BOOL | Build one fat shared library (default is OFF) | -| **CMAKE_BUILD_TYPE** : STRING | Build PhASAR in 'Debug', 'RelWithDebInfo' or 'Release' mode (default is 'Debug') | -| **CMAKE_INSTALL_PREFIX** : PATH | Path where PhASAR will be installed if "ninja install” is invoked or the “install” target is built (default is /usr/local/phasar) | -| **PHASAR_CUSTOM_CONFIG_INSTALL_DIR** : PATH | If set, customizes the directory, where configuration files for PhASAR are installed (default is /usr/local/.phasar-config)| -| **PHASAR_ENABLE_DYNAMIC_LOG** : BOOL|Makes it possible to switch the logger on and off at runtime (default is ON)| -| **PHASAR_BUILD_DOC** : BOOL | Build PhASAR documentation (default is OFF) | -| **PHASAR_BUILD_UNITTESTS** : BOOL | Build PhASAR unit tests (default is ON) | -| **PHASAR_BUILD_IR** : BOOL | Build PhASAR IR (required for running the unit tests) (default is ON) | -| **PHASAR_BUILD_OPENSSL_TS_UNITTESTS** : BOOL | Build PhASAR unit tests that require OpenSSL (default is OFF) | -| **PHASAR_ENABLE_PAMM** : STRING | Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is Off) | -| **PHASAR_ENABLE_PIC** : BOOL | Build Position-Independed Code (default is ON) | -| **PHASAR_ENABLE_WARNINGS** : BOOL | Enable compiler warnings (default is ON) | -| **CMAKE_CXX_STANDARD** : INT|Build phasar in C++17 or C++20 mode (default is 17)| - -You can use these parameters either directly or modify the installer-script `bootstrap.sh` - -#### A Remark on Compile Time - -C++'s long compile times are always a pain. As shown in the above, when using cmake the compilation can easily be run in parallel, resulting in shorter compilation times. Make use of it! - -### Running a Test Solver - -To test if everything works as expected please run the following command: - -`$ phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest` - -You can find the `phasar-cli` tool in the build-tree under `tools/phasar-cli`. - -If you obtain output other than a segmentation fault or an exception terminating the program abnormally everything works as expected. - -### Building PhASAR on a MacOS System - -Due to unfortunate updates to MacOS and the handling of C++, especially on the newer M1 processors, we can't support native development on Mac. -The easiest solution to develop PhASAR on a Mac right now is to use [dockers development environments](https://docs.docker.com/desktop/dev-environments/). Clone this repository as described in their documentation. Afterwards, you have to login once manually, as a root user by running `docker exec -it -u root /bin/bash` to complete the rest of the build process as described in this readme (install submodules, run bootstrap.sh, ...). -Now you can just attach your docker container to VS Code or any other IDE, which supports remote development. - -## Installation - -PhASAR can be installed using the installer scripts as explained in the following. -However, you do not need to install PhASAR in order to use it. - -### Installing PhASAR on an Ubuntu System - -In the following, we would like to give an complete example of how to install -PhASAR using an Ubuntu or Unix-like system. - -Therefore, we provide an installation script. To install PhASAR, just navigate to the top-level -directory of PhASAR and use the following command: - -```bash -./bootstrap.sh --install -``` - -The bootstrap script may ask for superuser permissions. - -Done! - -If You have already built phasar, you can just invoke -```bash -sudo ninja install -``` +Please refer to [BUILD.md](./BUILD.md) for instructions on how to build PhASAR. ## How to use PhASAR? diff --git a/examples/how-to/00-load-llvm-ir/CMakeLists.txt b/examples/how-to/00-load-llvm-ir/CMakeLists.txt new file mode 100644 index 0000000000..9d572feff4 --- /dev/null +++ b/examples/how-to/00-load-llvm-ir/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(load-llvm-ir) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(load-llvm-ir main.cpp) +target_link_libraries(load-llvm-ir PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_load_llvm_ir + DEPENDS load-llvm-ir + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/simple.ll" + ) + + add_dependencies(run_sample_programs run_load_llvm_ir) +endif() diff --git a/examples/how-to/00-load-llvm-ir/README.md b/examples/how-to/00-load-llvm-ir/README.md new file mode 100644 index 0000000000..e3cbaa9821 --- /dev/null +++ b/examples/how-to/00-load-llvm-ir/README.md @@ -0,0 +1,24 @@ +# Load LLVM IR + +Shows, how you can use PhASAR to load and manage a LLVM IR module. + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 00-load-llvm-ir root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 00-load-llvm-ir/build folder: +./load-llvm-ir ../../../llvm-hello-world/target/simple.ll +``` diff --git a/examples/how-to/00-load-llvm-ir/main.cpp b/examples/how-to/00-load-llvm-ir/main.cpp new file mode 100644 index 0000000000..98179ca279 --- /dev/null +++ b/examples/how-to/00-load-llvm-ir/main.cpp @@ -0,0 +1,55 @@ +#include "phasar/PhasarLLVM/DB.h" // For LLVMProjectIRDB +#include "phasar/PhasarLLVM/Passes.h" // For GeneralStatisticsAnalysis +#include "phasar/PhasarLLVM/Utils.h" // For llvmIRToString() + +#include "llvm/IR/InstIterator.h" // For llvm::instructions() + +static void printIRStats(psr::LLVMProjectIRDB &IRDB); + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: load-llvm-ir \n"; + return 1; + } + + // The LLVMProjectIRDB loads and manages an LLVM-IR module. + // You can load both .ll (human readable) and .bc (smaller, faster load-times) + // files. + // If you already have an llvm::Module*, you can also pass it here. + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + // If phasar yould not load the IR, we should exit. + // Phasar has already printed an error message to the terminal. + return 1; + } + + // ======== + // Now, you can work with the module + + printIRStats(IRDB); + + // Inspect the module (see also llvm-hello-world) + + auto *F = IRDB.getFunctionDefinition("main"); + if (!F) { + llvm::errs() << "error: could not find function 'main'\n"; + return 1; + } + + llvm::outs() << "--------------- Instructions of 'main' ---------------\n"; + + for (const auto &Inst : llvm::instructions(F)) { + // Phasar annotates all instructions (and global variables) with IRDB-wide + // unique integer Ids: + auto InstId = IRDB.getInstructionId(&Inst); + + llvm::outs() << '#' << InstId << ": " << psr::llvmIRToString(&Inst) << '\n'; + + // TODO: Analyze instruction 'Inst' here. + } +} + +static void printIRStats(psr::LLVMProjectIRDB &IRDB) { + psr::GeneralStatisticsAnalysis Stats; + llvm::outs() << Stats.runOnModule(*IRDB.getModule()) << '\n'; +} diff --git a/examples/how-to/01-build-type-hierarchy/CMakeLists.txt b/examples/how-to/01-build-type-hierarchy/CMakeLists.txt new file mode 100644 index 0000000000..f6e0303099 --- /dev/null +++ b/examples/how-to/01-build-type-hierarchy/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(build-type-hierarchy) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(build-type-hierarchy main.cpp) +target_link_libraries(build-type-hierarchy PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_build_type_hierarchy + DEPENDS build-type-hierarchy + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll" + ) + + add_dependencies(run_sample_programs run_build_type_hierarchy) +endif() diff --git a/examples/how-to/01-build-type-hierarchy/README.md b/examples/how-to/01-build-type-hierarchy/README.md new file mode 100644 index 0000000000..88ade55b42 --- /dev/null +++ b/examples/how-to/01-build-type-hierarchy/README.md @@ -0,0 +1,24 @@ +# Build Type Hierarchy + +Shows, how you can use PhASAR to build and use a type hierarchy from a LLVM IR module. + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 01-build-type-hierarchy root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 01-build-type-hierarchy/build folder: +./build-type-hierarchy ../../../llvm-hello-world/target/class_hierarchy.ll +``` diff --git a/examples/how-to/01-build-type-hierarchy/main.cpp b/examples/how-to/01-build-type-hierarchy/main.cpp new file mode 100644 index 0000000000..28412a25ff --- /dev/null +++ b/examples/how-to/01-build-type-hierarchy/main.cpp @@ -0,0 +1,47 @@ +#include "phasar/PhasarLLVM/DB.h" +#include "phasar/PhasarLLVM/TypeHierarchy.h" + +#include "llvm/Demangle/Demangle.h" + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: build-type-hierarchy \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // Build the type hierarchy. + // Note that this DIBasedTypeHierarchy requires debug information (DI) to be + // embedded into the LLVM IR. You can achieve this by passing -g to clang. + psr::DIBasedTypeHierarchy TH(IRDB); + + for (const auto *ClassTy : TH.getAllTypes()) { + llvm::outs() << "Found class type " << ClassTy->getName() << " (" + << TH.getTypeName(ClassTy) << ")\n"; + llvm::outs() << "> demangled name: " + << llvm::demangle(TH.getTypeName(ClassTy).str()) << '\n'; + } + llvm::outs() << '\n'; + + // Try to find class 'A' + const auto *ClassA = TH.getType("_ZTS1A"); + + // If TH does not find, it returns nullptr. + + if (ClassA != nullptr) { + // Get the (transitive) sub-types of ClassA + for (const auto *ClassTy : TH.subTypesOf(ClassA)) { + llvm::outs() << "Class " << ClassTy->getName() + << " is a (transitive) sub-type of A\n"; + + // You can also check, whether a type is a (transitive) sub-type of + // another type: + assert(TH.isSubType(ClassA, ClassTy)); + } + } +} diff --git a/examples/how-to/02-build-call-graph/CMakeLists.txt b/examples/how-to/02-build-call-graph/CMakeLists.txt new file mode 100644 index 0000000000..56d5fc25d8 --- /dev/null +++ b/examples/how-to/02-build-call-graph/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(build-call-graph) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(build-llvm-based-icfg build_llvm_based_icfg.cpp) +target_link_libraries(build-llvm-based-icfg PRIVATE phasar::phasar) + +add_executable(build-llvm-based-call-graph build_llvm_based_call_graph.cpp) +target_link_libraries(build-llvm-based-call-graph PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_build_call_graph + DEPENDS build-llvm-based-icfg build-llvm-based-call-graph + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll" + ) + + add_dependencies(run_sample_programs run_build_call_graph) +endif() diff --git a/examples/how-to/02-build-call-graph/README.md b/examples/how-to/02-build-call-graph/README.md new file mode 100644 index 0000000000..3d9981c109 --- /dev/null +++ b/examples/how-to/02-build-call-graph/README.md @@ -0,0 +1,38 @@ +# Build CallGraph + +Shows several ways, how you can use PhASAR to build and use a call graph from a LLVM IR module. + +You may look at the different C++ source files to see, how you can build a call graph using PhASAR. +You may want to start with [build_llvm_based_icfg.cpp](./build_llvm_based_icfg.cpp). + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 02-build-call-graph root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 02-build-call-graph/build folder: +./build-llvm-based-icfg ../../../llvm-hello-world/target/class_hierarchy.ll + +./build-llvm-based-call-graph ../../../llvm-hello-world/target/class_hierarchy.ll +``` + +### Visualizing the CallGraph + +The test programs show, how you can export a call-graph to a dot-graph. +You can use the `dot` command-line tool (get this by, e.g., invoking `apt install graphviz` or similar). + +The call-graph obtained from the example program on the sample `class_hierarchy.ll` should look similar to this: + +![Sample Call-Graph](./img/cg.svg) diff --git a/examples/how-to/02-build-call-graph/build_llvm_based_call_graph.cpp b/examples/how-to/02-build-call-graph/build_llvm_based_call_graph.cpp new file mode 100644 index 0000000000..5704f31eee --- /dev/null +++ b/examples/how-to/02-build-call-graph/build_llvm_based_call_graph.cpp @@ -0,0 +1,87 @@ +#include "phasar/PhasarLLVM/ControlFlow.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h" +#include "phasar/PhasarLLVM/DB.h" +#include "phasar/PhasarLLVM/TypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils.h" + +#include "llvm/Demangle/Demangle.h" + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: build-llvm-based-call-graph \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + if (!IRDB.getFunctionDefinition("main")) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + // We may wish to use type-information for construcing the call-graph + psr::DIBasedTypeHierarchy TH(IRDB); + + // Needed to resolve indirect calls to C++ virtual functions + psr::LLVMVFTableProvider VTP(IRDB); + + // The resolver defines, how the call-graph construction algorithm resolves + // indirect calls. + // This comprises calls through a function pointer, as well as virtual + // functions. Here, we select the Rapid Type Analysis that requires a + // type-hierarchy as input. + // + // You can also write your own resolver by creating a class that inherits from + // the psr::Resolver interface. + psr::RTAResolver Resolver(&IRDB, &VTP, &TH); + + // You must specify at least one function as entry-point. The + // LLVMBasedICFG will only consider those functions for the call-graph + // that are reachable from at least on eof the entry-points. + auto CG = psr::buildLLVMBasedCallGraph(IRDB, Resolver, {"main"}); + + // Iterate over all call-sites: + for (const auto *Call : CG.getAllVertexCallSites()) { + if (Call->isDebugOrPseudoInst()) { + // We may with to skip the auto-generated debug-intrinsics + continue; + } + + llvm::outs() << "Found call-site: " << psr::llvmIRToString(Call) << '\n'; + + // The probably most important function: getCalleesOfCallAt() + for (const auto *CalleeFun : CG.getCalleesOfCallAt(Call)) { + llvm::outs() << "> calling " + << llvm::demangle(CalleeFun->getName().str()) << '\n'; + } + llvm::outs() << '\n'; + } + + llvm::outs() << "--------------------------\n"; + + // You can also go the other way around: + for (const auto *Fun : CG.getAllVertexFunctions()) { + llvm::outs() << "Found Function: " << llvm::demangle(Fun->getName().str()) + << '\n'; + + // The probably second-most important function: getCallersOf() + for (const auto *CallSite : CG.getCallersOf(Fun)) { + llvm::outs() << "> called from " << psr::llvmIRToString(CallSite) + << '\n'; + } + llvm::outs() << '\n'; + } + + // You can create an LLVMBasedICFG from an already existing call-graph: + psr::LLVMBasedICFG ICFG(std::move(CG), &IRDB); + + llvm::outs() << "--------------------------\n"; + + // You can export the call-graph as dot, such that you can display it + // using a graphviz viewer: + ICFG.print(); +} diff --git a/examples/how-to/02-build-call-graph/build_llvm_based_icfg.cpp b/examples/how-to/02-build-call-graph/build_llvm_based_icfg.cpp new file mode 100644 index 0000000000..61a5b438bd --- /dev/null +++ b/examples/how-to/02-build-call-graph/build_llvm_based_icfg.cpp @@ -0,0 +1,81 @@ +#include "phasar/PhasarLLVM/ControlFlow.h" +#include "phasar/PhasarLLVM/DB.h" +#include "phasar/PhasarLLVM/TypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils.h" + +#include "llvm/Demangle/Demangle.h" + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: build-llvm-based-icfg \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + if (!IRDB.getFunctionDefinition("main")) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + // We may wish to use type-information for construcing the call-graph + psr::DIBasedTypeHierarchy TH(IRDB); + + // The easiest way of getting a call graph is by construcing an + // inter-procedural control-flow graph (ICFG): + // + // You can select the call-graph resolver algorithm using the + // CallGraphAnalysisType enum. The LLVMBasedICFG will create required + // data-structures that you don't explicitly pass in, on demand. + // Here, we select the Rapid Type Analysis that requires a type-hierarchy as + // input. + // + // You must specify at least one function as entry-point. The LLVMBasedICFG + // will only consider those functions for the call-graph that are + // (transitively) reachable from at least on of the entry-points. + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::RTA, {"main"}, + &TH); + const auto &CG = ICFG.getCallGraph(); + + // Iterate over all call-sites: + for (const auto *Call : CG.getAllVertexCallSites()) { + if (Call->isDebugOrPseudoInst()) { + // We may wish to skip the auto-generated debug-intrinsics + continue; + } + + llvm::outs() << "Found call-site: " << psr::llvmIRToString(Call) << '\n'; + + // The probably most important function: getCalleesOfCallAt() + for (const auto *CalleeFun : CG.getCalleesOfCallAt(Call)) { + llvm::outs() << "> calling " + << llvm::demangle(CalleeFun->getName().str()) << '\n'; + } + llvm::outs() << '\n'; + } + + llvm::outs() << "--------------------------\n"; + + // You can also go the other way around: + for (const auto *Fun : CG.getAllVertexFunctions()) { + llvm::outs() << "Found Function: " << llvm::demangle(Fun->getName().str()) + << '\n'; + + // The probably second-most important function: getCallersOf() + for (const auto *CallSite : CG.getCallersOf(Fun)) { + llvm::outs() << "> called from " << psr::llvmIRToString(CallSite) + << '\n'; + } + llvm::outs() << '\n'; + } + + llvm::outs() << "--------------------------\n"; + + // You can also export the call-graph as dot, such that you can display it + // using a graphviz viewer: + ICFG.print(); +} diff --git a/examples/how-to/02-build-call-graph/img/cg.svg b/examples/how-to/02-build-call-graph/img/cg.svg new file mode 100644 index 0000000000..300f05cae6 --- /dev/null +++ b/examples/how-to/02-build-call-graph/img/cg.svg @@ -0,0 +1,298 @@ + + + + + + +CallGraph + + + +0 + +_ZN1BD2Ev + + + +3 + +_ZN1AD2Ev + + + +0->3 + + +call void @_ZN1AD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) | ID: 90 + + + +11 + +llvm.dbg.declare + + + +0->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 88 + + + +1 + +main + + + +1->0 + + +call void @_ZN1BD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %b) | ID: 47 + + + +1->0 + + +call void @_ZN1BD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %b) | ID: 32 + + + +2 + +_ZN1BC2Ev + + + +1->2 + + +call void @_ZN1BC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %b) | ID: 19 + + + +4 + +_ZN1CD2Ev + + + +1->4 + + +call void @_ZN1CD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %c) | ID: 45 + + + +1->4 + + +call void @_ZN1CD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %c) | ID: 31 + + + +5 + +_ZN1CC2Ev + + + +1->5 + + +call void @_ZN1CC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %c) | ID: 22 + + + +7 + +_ZN1B3fooEv + + + +1->7 + + +invoke void @_ZN1B3fooEv(ptr noundef nonnull align 8 dereferenceable(8) %b) +          to label %invoke.cont unwind label %lpad | ID: 20 + + + +1->7 + + +invoke void %1(ptr noundef nonnull align 8 dereferenceable(8) %0) +          to label %invoke.cont3 unwind label %lpad1 | ID: 30 + + + +1->11 + + +call void @llvm.dbg.declare(metadata ptr %c, metadata | ID: 21 + + + +1->11 + + +call void @llvm.dbg.declare(metadata ptr %b, metadata | ID: 18 + + + +1->11 + + +call void @llvm.dbg.declare(metadata ptr %a, metadata | ID: 24 + + + +12 + +_ZN1C3fooEv + + + +1->12 + + +invoke void %1(ptr noundef nonnull align 8 dereferenceable(8) %0) +          to label %invoke.cont3 unwind label %lpad1 | ID: 30 + + + +1->12 + + +invoke void @_ZN1C3fooEv(ptr noundef nonnull align 8 dereferenceable(8) %c) +          to label %invoke.cont2 unwind label %lpad1 | ID: 23 + + + +9 + +_ZN1AC2Ev + + + +2->9 + + +call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) | ID: 58 + + + +2->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 56 + + + +3->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 107 + + + +4->3 + + +call void @_ZN1AD2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) | ID: 84 + + + +4->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 82 + + + +5->9 + + +call void @_ZN1AC2Ev(ptr noundef nonnull align 8 dereferenceable(8) %this1) | ID: 71 + + + +5->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 69 + + + +6 + +__psrCRuntimeGlobalDtorsModel + + + +8 + +puts + + + +7->8 + + +%call = call i32 @puts(ptr noundef @.str) | ID: 65 + + + +7->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 63 + + + +9->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 94 + + + +10 + +__psrCRuntimeGlobalCtorsModel + + + +10->1 + + +%2 = call i32 @main() | ID: 124 + + + +10->6 + + +call void @__psrCRuntimeGlobalDtorsModel() | ID: 125 + + + +12->8 + + +%call = call i32 @puts(ptr noundef @.str.1) | ID: 78 + + + +12->11 + + +call void @llvm.dbg.declare(metadata ptr %this.addr, metadata | ID: 76 + + + diff --git a/examples/how-to/03-create-alias-info/CMakeLists.txt b/examples/how-to/03-create-alias-info/CMakeLists.txt new file mode 100644 index 0000000000..d47aa2d3a2 --- /dev/null +++ b/examples/how-to/03-create-alias-info/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(create-alias-info) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(create-alias-info main.cpp) +target_link_libraries(create-alias-info PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_create_alias_info + DEPENDS create-alias-info + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/pointers.ll" + ) + + add_dependencies(run_sample_programs run_create_alias_info) +endif() diff --git a/examples/how-to/03-create-alias-info/README.md b/examples/how-to/03-create-alias-info/README.md new file mode 100644 index 0000000000..e9ef4509fe --- /dev/null +++ b/examples/how-to/03-create-alias-info/README.md @@ -0,0 +1,24 @@ +# Create Alias Information + +Shows, how you can use PhASAR to generate alias information from a LLVM IR module. + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 03-create-alias-info root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 03-create-alias-info/build folder: +./create-alias-info ../../../llvm-hello-world/target/pointers.ll +``` diff --git a/examples/how-to/03-create-alias-info/main.cpp b/examples/how-to/03-create-alias-info/main.cpp new file mode 100644 index 0000000000..b45e969418 --- /dev/null +++ b/examples/how-to/03-create-alias-info/main.cpp @@ -0,0 +1,84 @@ +#include "phasar/PhasarLLVM/DB.h" +#include "phasar/PhasarLLVM/Pointer.h" +#include "phasar/PhasarLLVM/Utils.h" + +#include "llvm/IR/InstIterator.h" + +#include + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: create-alias-info \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // The easiest way of getting alias information is using the LLVMAliasSet: + psr::LLVMAliasSet AS(&IRDB, /*UseLazyEvaluation=*/false); + + // PhASAR APIs usually do not care, which alias-info implementation you use. + // They take a type-erased reference to any alias-info object. + // You can implicitly convert a pointer to any compatible alias-info object to + // an LLVMAliasInfoRef. + // Since the LLVMAliasInfoRef is a non-owning reference, you must make sure + // that the actual LLVMAliasSet object outlives any use of the references to + // it. + psr::LLVMAliasInfoRef ASRef = &AS; + + // You can print and load alias information from/to JSON: + AS.printAsJson(); + + const auto *MainF = IRDB.getFunctionDefinition("main"); + if (!MainF) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + // Manually printing the alias sets: + + for (const auto &Inst : llvm::instructions(MainF)) { + if (!Inst.getType()->isPointerTy()) { + // For aliasing, we only care about pointers... + continue; + } + + // Retrieve the aliases of the result of the instruction Inst (first + // parameter) at the program location determined by Inst (second parameter). + // + // Implementations may ignore the second parameter. + auto AliasesOfInstAtInst = AS.getAliasSet(&Inst, &Inst); + + llvm::outs() << "For pointer " << psr::llvmIRToString(&Inst) << ":\n"; + for (const auto *Alias : *AliasesOfInstAtInst) { + llvm::outs() << "> aliasing " << psr::llvmIRToShortString(Alias) << '\n'; + + // You can also check, whether two pointers are (potentially) aliasing: + assert(AS.alias(&Inst, Alias, &Inst)); + } + + // Retrieve a filtered alias set only containing allocation-sites for the + // aliases of the result of the instruction Inst (first parameter), further + // filtered to not contain allocation-sites from other functions (second + // parameter), at the program location determined by Inst (third + // parameter). + // + // Implementations may ignore the third parameter. + auto ReachableAllocSites = + AS.getReachableAllocationSites(&Inst, /*IntraProcOnly=*/true, &Inst); + for (const auto *AllocSite : *ReachableAllocSites) { + llvm::outs() << "> reachable alloc-site " + << psr::llvmIRToShortString(AllocSite) << '\n'; + + // You can also check, whether a pointer is in the reachable + // allocation-sites of another pointer: + assert(AS.isInReachableAllocationSites(&Inst, AllocSite, + /*IntraProcOnly=*/true, &Inst)); + } + llvm::outs() << '\n'; + } +} diff --git a/examples/how-to/04-run-ifds-analysis/CMakeLists.txt b/examples/how-to/04-run-ifds-analysis/CMakeLists.txt new file mode 100644 index 0000000000..6b8c99365b --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(run-ifds-analysis) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(run-ifds-analysis-simple simple.cpp) +target_link_libraries(run-ifds-analysis-simple PRIVATE phasar::phasar) + +add_executable(run-ifds-analysis-helper-analyses helper-analyses.cpp) +target_link_libraries(run-ifds-analysis-helper-analyses PRIVATE phasar::phasar) + +add_executable(run-ifds-analysis-otf-reporter otf-reporter.cpp) +target_link_libraries(run-ifds-analysis-otf-reporter PRIVATE phasar::phasar) + +add_executable(run-ifds-analysis-ifds-solver ifds-solver.cpp) +target_link_libraries(run-ifds-analysis-ifds-solver PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_run_ifds_analysis + DEPENDS run-ifds-analysis-simple run-ifds-analysis-helper-analyses run-ifds-analysis-otf-reporter run-ifds-analysis-ifds-solver + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + ) + + add_dependencies(run_sample_programs run_run_ifds_analysis) +endif() diff --git a/examples/how-to/04-run-ifds-analysis/README.md b/examples/how-to/04-run-ifds-analysis/README.md new file mode 100644 index 0000000000..d2a6cffa2b --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/README.md @@ -0,0 +1,34 @@ +# Run an IFDS Analysis + +Shows several ways, how you can use PhASAR to run an already existing IFDS analysis on a LLVM IR module. +For this example, we selected the `IFDSTaintAnalysis`. + +You may look at the different C++ source files to see, how you can run an IFDS taint analysis using PhASAR. +We suggest to start with the simplest examples [simple.cpp](./simple.cpp) and [helper-analyses.cpp](./helper-analyses.cpp). + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 04-run-ifds-analysis root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 04-run-ifds-analysis/build folder: +./run-ifds-analysis-simple ../../../llvm-hello-world/target/taint.ll + +./run-ifds-analysis-helper-analyses ../../../llvm-hello-world/target/taint.ll + +./run-ifds-analysis-otf-reporter ../../../llvm-hello-world/target/taint.ll + +./run-ifds-analysis-ifds-solver ../../../llvm-hello-world/target/taint.ll +``` diff --git a/examples/how-to/04-run-ifds-analysis/helper-analyses.cpp b/examples/how-to/04-run-ifds-analysis/helper-analyses.cpp new file mode 100644 index 0000000000..1469349163 --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/helper-analyses.cpp @@ -0,0 +1,52 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/PhasarLLVM.h" // For the HelperAnalyses +#include "phasar/PhasarLLVM/DataFlow.h" // For the IFDSTaintAnalysis +#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-simple \n"; + return 1; + } + + using namespace std::string_literals; + std::vector EntryPoints = {"main"s}; + + // Instead of creating all the helper analyses ourselves, we can just use the + // HelperAnalyses class. It will create the necessary information on-demand. + // + // You can customize the underlying algorithms by passing a + // HelperAnalysisConfig as third parameter + psr::HelperAnalyses HA(Argv[1], EntryPoints); + if (!HA.getProjectIRDB()) { + return 1; + } + + // Create the taint configuration + psr::LLVMTaintConfig TC(HA.getProjectIRDB()); + TC.print(); + llvm::outs() << "------------------------\n"; + + // Create the taint analysis problem: + // The utility function createAnalysisProblem() simplifies creating an + // analysis problem with a HelperAnalyses object. It automatically passes the + // right arguments + auto TaintProblem = psr::createAnalysisProblem( + HA, &TC, EntryPoints, /*TaintMainArgs=*/false); + + // Solving the TaintProblem. This may take some time, depending on the size of + // the ICFG + psr::solveIFDSProblem(TaintProblem, HA.getICFG()); + + // After we have solved the TaintProblem, we can now inspect the detected + // leaks: + for (const auto &[LeakInst, LeakFacts] : TaintProblem.Leaks) { + llvm::outs() << "Detected taint leak at " << psr::llvmIRToString(LeakInst) + << '\n'; + for (const auto *Fact : LeakFacts) { + llvm::outs() << "> leaking fact " << psr::llvmIRToShortString(Fact) + << '\n'; + } + llvm::outs() << '\n'; + } +} diff --git a/examples/how-to/04-run-ifds-analysis/ifds-solver.cpp b/examples/how-to/04-run-ifds-analysis/ifds-solver.cpp new file mode 100644 index 0000000000..2ea821d332 --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/ifds-solver.cpp @@ -0,0 +1,73 @@ +#include "phasar/DataFlow.h" // For the IFDSSolver +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IFDSTaintAnalysis +#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet +#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig + +#include +#include + +static constexpr std::string_view Spinner[] = {"⠙", "⠹", "⠸", "⠼", + "⠴", "⠦", "⠇", "⠿"}; + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-ifds-solver \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // The IFDSTaintAnalysis requires alias information, so create it here + psr::LLVMAliasSet AS(&IRDB); + + // Create the taint configuration + psr::LLVMTaintConfig TC(IRDB); + TC.print(); + llvm::outs() << "------------------------\n"; + + // Create the taint analysis problem + psr::IFDSTaintAnalysis TaintProblem(&IRDB, &AS, &TC, {"main"}, + /*TaintMainArgs=*/false); + + // Create the ICFG + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}, + nullptr, &AS); + + // To solve the taint problem, we now create an instance of the IFDSSolver. + // The function psr::solveIFDSProblem() uses this solver internally as well. + // Having the solver explicitly, allows more control over the solving process: + psr::IFDSSolver Solver(&TaintProblem, &ICFG); + + // The simple solution. You don't really need an explicit solver for this: + // Solver.solve(); + + // Have more control over the solving process: + if (Solver.initialize()) { + int i = 0; + + // Perform the next 10 analysis steps, while we still have some + while (Solver.nextN(10)) { + // Perform some intermediate task *during* the solving process. + // We could also interrupt the solver at any time and continue later. + llvm::outs() << "\b\b" << Spinner[i] << ' '; + i = (i + 1) % std::size(Spinner); + + // Wait a bit, such that we have time to see the beautiful animation for + // our tiny example target programs: + using namespace std::chrono_literals; + std::this_thread::sleep_for(100ms); + } + + Solver.finalize(); + llvm::outs() << "\nSolving finished\n"; + } + + // Here, we could loop over TaintProblem.Leaks. Instead, we will now use + // the Solver to dump the whole raw IFDS results: + Solver.dumpResults(); +} diff --git a/examples/how-to/04-run-ifds-analysis/otf-reporter.cpp b/examples/how-to/04-run-ifds-analysis/otf-reporter.cpp new file mode 100644 index 0000000000..e2e0dda622 --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/otf-reporter.cpp @@ -0,0 +1,65 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IFDSTaintAnalysis +#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet +#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig + +namespace { +/// A listener that gets notified, whenever the taint analysis detects a leak +class LeakReporter + : public psr::AnalysisPrinterBase { + + /// This function will be called once for each detected leak, **while the + /// analysis is still running**. + void doOnResult(const llvm::Instruction *LeakInst, + const llvm::Value *LeakFact, + psr::BinaryDomain /*LatticeElement*/, + psr::DataFlowAnalysisType /*AnalysisType*/) override { + llvm::outs() << "Detected taint leak at " << psr::llvmIRToString(LeakInst) + << '\n'; + llvm::outs() << "> leaking fact " << psr::llvmIRToShortString(LeakFact) + << "\n\n"; + } +}; +} // namespace + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-otf-reporter \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // The IFDSTaintAnalysis requires alias information, so create it here + psr::LLVMAliasSet AS(&IRDB); + + // Create the taint configuration + psr::LLVMTaintConfig TC(IRDB); + TC.print(); + llvm::outs() << "------------------------\n"; + + // Create the taint analysis problem + psr::IFDSTaintAnalysis TaintProblem(&IRDB, &AS, &TC, {"main"}, + /*TaintMainArgs=*/false); + + // Create the ICFG + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}, + nullptr, &AS); + + // We want to get notified, whenever the taint analysis detects a leak: + LeakReporter Reporter; + TaintProblem.setAnalysisPrinter(&Reporter); + + // Solving the TaintProblem. This may take some time, depending on the size of + // the ICFG + psr::solveIFDSProblem(TaintProblem, ICFG); + + // Don't need to loop over the leaks anymore. We have already intercepted all + // incoming leaks with our Reporter +} diff --git a/examples/how-to/04-run-ifds-analysis/simple.cpp b/examples/how-to/04-run-ifds-analysis/simple.cpp new file mode 100644 index 0000000000..f550dd171e --- /dev/null +++ b/examples/how-to/04-run-ifds-analysis/simple.cpp @@ -0,0 +1,71 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IFDSTaintAnalysis +#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet +#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-simple \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // The IFDSTaintAnalysis requires alias information, so create it here + psr::LLVMAliasSet AS(&IRDB); + + // To tell the IFDSTaintAnalysis, which functions are actually sources, sinks + // and sanitizers, we use the LLVMTaintConfig class. + // + // There are several ways of getting a taint configuration into this class: + // - Loading a JSON file + // - Specifying call-backs + // - Annotating the target code + // + // For simplicity, we selected the annotated target code here (checkout the + // taint.cpp target program in llvm-hello-world/target) + psr::LLVMTaintConfig TC(IRDB); + TC.print(); + llvm::outs() << "------------------------\n"; + + // Here, we instantiate the taint analysis problem. + // We need to pass all information that the taint analysis requires: The IRDB, + // alias info, taint config, and the functions where the analysis should + // start. The IFDS solver will walk the inter-procedural control-flow graph + // (ICFG) to analyze all statements and functions that are reachable from the + // entrypoints. + psr::IFDSTaintAnalysis TaintProblem(&IRDB, &AS, &TC, {"main"}, + /*TaintMainArgs=*/false); + + // To solve the TaintProblem, we need an ICFG. + // Checkout the example 02-build-call-graph for details. + // Here, we select the OTF call-graph algorithm, which uses alias information + // for indirect call resolution. + // + // Since we already have computed alias information, it would be wasteful to + // let the LLVMBasedICFG compute the alias info again, so we pass the AS here. + // The OTF analysis does not require a type-hierarchy. + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}, + nullptr, &AS); + + // Solving the TaintProblem. This may take some time, depending on the size of + // the ICFG + psr::solveIFDSProblem(TaintProblem, ICFG); + + // After we have solved the TaintProblem, we can now inspect the detected + // leaks: + for (const auto &[LeakInst, LeakFacts] : TaintProblem.Leaks) { + llvm::outs() << "Detected taint leak at " << psr::llvmIRToString(LeakInst) + << '\n'; + for (const auto *Fact : LeakFacts) { + llvm::outs() << "> leaking fact " << psr::llvmIRToShortString(Fact) + << '\n'; + } + llvm::outs() << '\n'; + } +} diff --git a/examples/how-to/05-run-ide-analysis/CMakeLists.txt b/examples/how-to/05-run-ide-analysis/CMakeLists.txt new file mode 100644 index 0000000000..cc71bd81a8 --- /dev/null +++ b/examples/how-to/05-run-ide-analysis/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(run-ide-analysis) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(run-ide-analysis-simple simple.cpp) +target_link_libraries(run-ide-analysis-simple PRIVATE phasar::phasar) + +add_executable(run-ide-analysis-helper-analyses helper-analyses.cpp) +target_link_libraries(run-ide-analysis-helper-analyses PRIVATE phasar::phasar) + +add_executable(run-ide-analysis-ide-solver ide-solver.cpp) +target_link_libraries(run-ide-analysis-ide-solver PRIVATE phasar::phasar) + +if (TARGET run_sample_programs) + add_custom_target(run_run_ide_analysis + DEPENDS run-ide-analysis-simple run-ide-analysis-helper-analyses run-ide-analysis-ide-solver + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll" + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll" + ) + + add_dependencies(run_sample_programs run_run_ide_analysis) +endif() diff --git a/examples/how-to/05-run-ide-analysis/README.md b/examples/how-to/05-run-ide-analysis/README.md new file mode 100644 index 0000000000..c46f5c5bce --- /dev/null +++ b/examples/how-to/05-run-ide-analysis/README.md @@ -0,0 +1,32 @@ +# Run an IDE Analysis + +Shows several ways, how you can use PhASAR to run an already existing IDE analysis on a LLVM IR module. +For this example, we selected the `IDELinearConstantAnalysis`. + +You may look at the different C++ source files to see, how you can run an IDE linear constant analysis using PhASAR. +We suggest to start with the simplest examples [simple.cpp](./simple.cpp) and [helper-analyses.cpp](./helper-analyses.cpp). + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 05-run-ide-analysis root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 05-run-ide-analysis/build folder: +./run-ide-analysis-simple ../../../llvm-hello-world/target/call2.ll + +./run-ide-analysis-helper-analyses ../../../llvm-hello-world/target/call2.ll + +./run-ide-analysis-ide-solver ../../../llvm-hello-world/target/call2.ll +``` diff --git a/examples/how-to/05-run-ide-analysis/helper-analyses.cpp b/examples/how-to/05-run-ide-analysis/helper-analyses.cpp new file mode 100644 index 0000000000..d2e1229948 --- /dev/null +++ b/examples/how-to/05-run-ide-analysis/helper-analyses.cpp @@ -0,0 +1,65 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/PhasarLLVM.h" // For the HelperAnalyses +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IDELinearConstantAnalysis + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-simple \n"; + return 1; + } + + // Similar to IFDS, you can also use the HelperAnalyses class to reduce some + // boilerplate code: + + using namespace std::string_literals; + std::vector EntryPoints = {"main"s}; + // Instead of creating all the helper analyses ourselves, we can just use the + // HelperAnalyses class. It will create the necessary information on-demand. + // + // You can customize the underlying algorithms by passing a + // HelperAnalysisConfig as third parameter + psr::HelperAnalyses HA(Argv[1], EntryPoints); + if (!HA.getProjectIRDB()) { + return 1; + } + + // Here, we instantiate the linear constant analysis problem. + // In contrast to the example in simple.cpp, we only need to pass the + // HelperAnalyses and the entry points (this may vary, depending on the + // analysis problem to solve) + auto LCAProblem = psr::createAnalysisProblem( + HA, EntryPoints); + + // Solving the LCAProblem. This may take some time, depending on the size of + // the ICFG + auto Results = psr::solveIDEProblem(LCAProblem, HA.getICFG()); + + // After we have solved the LCAProblem, we can now inspect the detected + // constants: + + const auto *MainF = HA.getProjectIRDB().getFunctionDefinition("main"); + if (!MainF) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + const auto *ExitOfMain = psr::getAllExitPoints(MainF).front(); + + // Get the analysis results right **after** main's return statement + const auto &AllConstantsAtMainExit = Results.resultsAt(ExitOfMain); + + llvm::outs() << "Detected constants at " << psr::llvmIRToString(ExitOfMain) + << ":\n"; + for (const auto &[LLVMVar, ConstVal] : AllConstantsAtMainExit) { + llvm::outs() << " " << psr::llvmIRToString(LLVMVar) << "\n --> "; + if (ConstVal.isBottom()) { + // A "bottom" value here means that the analysis does not know the value + // at this point and that any value may be possible. + + llvm::outs() << "\n\n"; + } else { + llvm::outs() << ConstVal << "\n\n"; + } + } +} diff --git a/examples/how-to/05-run-ide-analysis/ide-solver.cpp b/examples/how-to/05-run-ide-analysis/ide-solver.cpp new file mode 100644 index 0000000000..62099d4eb0 --- /dev/null +++ b/examples/how-to/05-run-ide-analysis/ide-solver.cpp @@ -0,0 +1,73 @@ +#include "phasar/DataFlow.h" // For the IDESolver +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IDELinearConstantAnalysis + +#include +#include + +static constexpr std::string_view Spinner[] = {"⠙", "⠹", "⠸", "⠼", + "⠴", "⠦", "⠇", "⠿"}; + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-simple \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // To solve the LCAProblem, we need an ICFG. + // Checkout the example 02-build-call-graph for details. + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}); + + // Here, we instantiate the linear constant analysis problem. + // We need to pass all information that the analysis requires: The IRDB, + // the ICFG, and the functions where the analysis should start. The IDE + // solver will walk the ICFG to analyze all statements and functions that are + // reachable from the entrypoints. + psr::IDELinearConstantAnalysis LCAProblem(&IRDB, &ICFG, {"main"}); + + // To solve the linear constant analysis problem, we now create an instance of + // the IDESolver. The function psr::solveIDEProblem() uses this solver + // internally as well. Having the solver explicitly, allows more control over + // the solving process: + psr::IDESolver Solver(&LCAProblem, &ICFG); + + // The simple solution. You don't really need an explicit solver for this: + // Solver.solve(); + + // Have more control over the solving process: + if (Solver.initialize()) { + int i = 0; + + // Perform the next 10 analysis steps, while we still have some + while (Solver.nextN(10)) { + // Perform some intermediate task *during* the solving process. + // We could also interrupt the solver at any time and continue later. + llvm::outs() << "\b\b" << Spinner[i] << ' '; + i = (i + 1) % std::size(Spinner); + + // Wait a bit, such that we have time to see the beautiful animation for + // our tiny example target programs: + using namespace std::chrono_literals; + std::this_thread::sleep_for(100ms); + } + + // In contrast to the IFDSSolver, finalize may take some time with IDE. + // It will still be significantly faster than the above loop. + Solver.finalize(); + llvm::outs() << "\nSolving finished\n"; + } + + // Accessing the results: + auto Results = Solver.getSolverResults(); + + // After we have solved the LCAProblem, we can now inspect the detected + // constants. Instead of manually looping, will now use + // the Solver to dump the whole raw IDE results: + Solver.dumpResults(); +} diff --git a/examples/how-to/05-run-ide-analysis/simple.cpp b/examples/how-to/05-run-ide-analysis/simple.cpp new file mode 100644 index 0000000000..98a78e658c --- /dev/null +++ b/examples/how-to/05-run-ide-analysis/simple.cpp @@ -0,0 +1,59 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For the IDELinearConstantAnalysis + +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: run-ifds-analysis-simple \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + // To solve the LCAProblem, we need an ICFG. + // Checkout the example 02-build-call-graph for details. + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}); + + // Here, we instantiate the linear constant analysis problem. + // We need to pass all information that the analysis requires: The IRDB, + // the ICFG, and the functions where the analysis should start. The IDE + // solver will walk the ICFG to analyze all statements and functions that are + // reachable from the entrypoints. + psr::IDELinearConstantAnalysis LCAProblem(&IRDB, &ICFG, {"main"}); + + // Solving the LCAProblem. This may take some time, depending on the size of + // the ICFG + auto Results = psr::solveIDEProblem(LCAProblem, ICFG); + + // After we have solved the LCAProblem, we can now inspect the detected + // constants: + + const auto *MainF = IRDB.getFunctionDefinition("main"); + if (!MainF) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + const auto *ExitOfMain = psr::getAllExitPoints(MainF).front(); + + // Get the analysis results right **after** main's return statement + const auto &AllConstantsAtMainExit = Results.resultsAt(ExitOfMain); + + llvm::outs() << "Detected constants at " << psr::llvmIRToString(ExitOfMain) + << ":\n"; + for (const auto &[LLVMVar, ConstVal] : AllConstantsAtMainExit) { + llvm::outs() << " " << psr::llvmIRToString(LLVMVar) << "\n --> "; + if (ConstVal.isBottom()) { + // A "bottom" value here means that the analysis does not know the value + // at this point and that any value may be possible. + + llvm::outs() << "\n\n"; + } else { + llvm::outs() << ConstVal << "\n\n"; + } + } +} diff --git a/examples/how-to/07-write-ifds-analysis/CMakeLists.txt b/examples/how-to/07-write-ifds-analysis/CMakeLists.txt new file mode 100644 index 0000000000..fe1839e072 --- /dev/null +++ b/examples/how-to/07-write-ifds-analysis/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(write-ifds-analysis) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(write-ifds-analysis-simple simple.cpp) +target_link_libraries(write-ifds-analysis-simple PRIVATE phasar::phasar) + + +if (TARGET run_sample_programs) + add_custom_target(run_write_ifds_analysis + DEPENDS write-ifds-analysis-simple + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + ) + + add_dependencies(run_sample_programs run_write_ifds_analysis) +endif() diff --git a/examples/how-to/07-write-ifds-analysis/README.md b/examples/how-to/07-write-ifds-analysis/README.md new file mode 100644 index 0000000000..f99548f5d1 --- /dev/null +++ b/examples/how-to/07-write-ifds-analysis/README.md @@ -0,0 +1,28 @@ +# Write an IFDS Analysis + +Shows, how you can use PhASAR to write an IFDS analysis to analyze LLVM IR. +For this example, we selected the versatile *taint analysis* as problem to implement. + +For more information, we suggest taking a look into PhASAR's Wiki: [Writing an IFDS Analysis](https://github.com/secure-software-engineering/phasar/wiki/Writing-an-IFDS-analysis). + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 07-write-ifds-analysis root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 07-write-ifds-analysis/build folder: +./write-ifds-analysis-simple ../../../llvm-hello-world/target/taint.ll + +``` diff --git a/examples/how-to/07-write-ifds-analysis/simple.cpp b/examples/how-to/07-write-ifds-analysis/simple.cpp new file mode 100644 index 0000000000..0a87764e9d --- /dev/null +++ b/examples/how-to/07-write-ifds-analysis/simple.cpp @@ -0,0 +1,146 @@ +#include "phasar/DataFlow.h" // For solveIFDSProblem() +#include "phasar/PhasarLLVM/ControlFlow.h" // For the LLVMBasedICFG +#include "phasar/PhasarLLVM/DB.h" // For the LLVMProjectIRDB +#include "phasar/PhasarLLVM/DataFlow.h" // For DefaultAliasAwareIFDSProblem, etc. +#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet +#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig + +namespace { + +void populateWithMayAliases(psr::LLVMAliasInfoRef AS, + std::set &Facts); + +/// To create a custom IFDS analysis, we must create a subclass of the +/// IFDSTabulationProblem. +/// The utility class DefaultAliasAwareIFDSProblem implements +/// IFDSTabulationProblem and already provides some default flow-functions and +/// handles aliasing, so that we can focus on the specifica of our analysis. +class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem { +public: + /// Constructor of the taint-analysis problem. Just forward all parameters to + /// the base-class and initialize the taint-config. + /// + /// The last parameter of the base-ctor denotes the special zero-value of the + /// IFDS problem. We use LLVMZeroValue for this. + explicit ExampleTaintAnalysis(const psr::LLVMProjectIRDB *IRDB, + psr::LLVMAliasInfoRef AS, + const psr::LLVMTaintConfig *Config, + std::vector EntryPoints) + : psr::DefaultAliasAwareIFDSProblem(IRDB, AS, std::move(EntryPoints), + psr::LLVMZeroValue::getInstance()), + Config(&psr::assertNotNull(Config)) {} + + /// 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 { + 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()); + + return Seeds; + }; + + /// 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 { + const auto *CS = llvm::cast(CallSite); + + // Process the effects of source or sink functions that are called + auto Gen = psr::getGeneratedFacts(*Config, CS, DestFun); + auto Leak = psr::getLeakedFacts(*Config, CS, DestFun); + auto Kill = psr::getSanitizedFacts(*Config, CS, DestFun); + + 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); + } + + // Since our analysis is alias-aware, we must handle aliasing here: + populateWithMayAliases(getAliasInfo(), Gen); + populateWithMayAliases(getAliasInfo(), Leak); + + // We have special behavior to communicate to the analysis solver, so create + // a flow-function that captures this behavior: + return lambdaFlow([this, CS, Gen{std::move(Gen)}, Leak{std::move(Leak)}, + Kill{std::move(Kill)}](d_t Source) -> container_type { + if (isZeroValue(Source)) { + // In case of a source, we generate the new taints from zero (Source). + return Gen; + } + + if (Leak.count(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)) { + // In case of a sanitizer, we kill tainted values (Source) that flow + // into the sanitizied parameters (Kill). + return {}; + } + + // Otherwise, the taint is unaffected from the source/sink/sanitizer, so + // propagate it as identity + return {Source}; + }); + } + + // We collect the leaking sink-calls here + llvm::DenseSet Leaks{}; + +private: + const psr::LLVMTaintConfig *Config{}; +}; + +// For all given facts, we add their aliases: +void populateWithMayAliases(psr::LLVMAliasInfoRef AS, + std::set &Facts) { + auto Tmp = Facts; + for (const auto *Fact : Facts) { + auto Aliases = AS.getAliasSet(Fact); + Tmp.insert(Aliases->begin(), Aliases->end()); + } + + Facts = std::move(Tmp); +} + +} // namespace + +// Invoke the analysis the same way as explained in 04-run-ifds-analysis: +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: write-ifds-analysis-simple \n"; + return 1; + } + + // Load the IR + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + psr::LLVMAliasSet AS(&IRDB); + psr::LLVMTaintConfig TC(IRDB); + TC.print(); + llvm::outs() << "------------------------\n"; + + ExampleTaintAnalysis TaintProblem(&IRDB, &AS, &TC, {"main"}); + + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}, + nullptr, &AS); + + psr::solveIFDSProblem(TaintProblem, ICFG); + + for (const auto *LeakInst : TaintProblem.Leaks) { + llvm::outs() << "Detected taint leak at " << psr::llvmIRToString(LeakInst) + << '\n'; + } +} diff --git a/examples/how-to/08-write-ide-analysis/CMakeLists.txt b/examples/how-to/08-write-ide-analysis/CMakeLists.txt new file mode 100644 index 0000000000..25d4088d49 --- /dev/null +++ b/examples/how-to/08-write-ide-analysis/CMakeLists.txt @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(write-ide-analysis) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(phasar REQUIRED CONFIG) + +add_executable(write-ide-analysis-simple simple.cpp) +target_link_libraries(write-ide-analysis-simple PRIVATE phasar::phasar) + + +if (TARGET run_sample_programs) + add_custom_target(run_write_ide_analysis + DEPENDS write-ide-analysis-simple + COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll" + ) + + add_dependencies(run_sample_programs run_write_ide_analysis) +endif() diff --git a/examples/how-to/08-write-ide-analysis/README.md b/examples/how-to/08-write-ide-analysis/README.md new file mode 100644 index 0000000000..bf67bf15e6 --- /dev/null +++ b/examples/how-to/08-write-ide-analysis/README.md @@ -0,0 +1,29 @@ +# Write an IDE Analysis + +Shows, how you can use PhASAR to write an IDE analysis to analyze LLVM IR. +For this example, we selected the linear constant analysis as problem to implement. + +The code example exactly matches the example from our Wiki: [Writing an IDE Analysis](https://github.com/secure-software-engineering/phasar/wiki/Writing-an-IDE-analysis). +So, we highly recommend taking a look there first. + +## Build + +This example program can be built using cmake. +It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR. + +```bash +# Invoked from the 08-write-ide-analysis root folder: +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +## Test + +You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/). + +```bash +# Invoked from the 08-write-ide-analysis/build folder: +./write-ide-analysis-simple ../../../llvm-hello-world/target/call2.ll + +``` diff --git a/examples/how-to/08-write-ide-analysis/simple.cpp b/examples/how-to/08-write-ide-analysis/simple.cpp new file mode 100644 index 0000000000..45443ced08 --- /dev/null +++ b/examples/how-to/08-write-ide-analysis/simple.cpp @@ -0,0 +1,484 @@ +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" +#include "phasar/Domain/LatticeDomain.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Argument.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" + +namespace { + +/// The domain for our analysis. We specialize the edge-value type l_t, i.e., +/// the type of constant values that are assigned to constant integer +/// variables. +struct ExampleIDELinearConstantAnalysisDomain : psr::LLVMAnalysisDomainDefault { + // We want to propagate constant integers. To make this domain a lattice, we + // wrap it into psr::LatticeDomain, adding special values for TOP and BOTTOM. + using l_t = psr::LatticeDomain; +}; + +/// To create a custom IDE analysis, we must create a subclass of the +/// IDETabulationProblem. +/// The utility class DefaultNoAliasIDEProblem implements +/// IDETabulationProblem and already provides some default flow-functions, so +/// that we can focus on the specifica of our analysis. +/// +/// \note For simplicity, we don't handle aliasing in this example; however, you +/// can use DefaultAliasAwareIDEProblem to handle aliasing in most cases. +class ExampleLinearConstantAnalysis + : public psr::DefaultNoAliasIDEProblem< + ExampleIDELinearConstantAnalysisDomain> { +public: + /// Constructor of the constant-analysis problem. Just forward all parameters + /// to the base-class. + /// + /// The last parameter of the base-ctor denotes the special zero-value, aka. + /// Λ, of the IDE problem. We use LLVMZeroValue for this. + explicit ExampleLinearConstantAnalysis(const psr::LLVMProjectIRDB *IRDB, + std::vector EntryPoints) + : DefaultNoAliasIDEProblem(IRDB, std::move(EntryPoints), + psr::LLVMZeroValue::getInstance()) {} + + /// Provides the initial seeds, i.e., the pairs that are assumed + /// 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 { + 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()); + + return Seeds; + }; + + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { + if (const auto *Alloca = llvm::dyn_cast(Curr)) { + // Freshly allocated variables hold no constant value + + auto *AT = Alloca->getAllocatedType(); + if (AT->isIntegerTy() || psr::isIntegerLikeType(AT)) { + return generateFromZero(Alloca); + } + } + + if (const auto *Store = llvm::dyn_cast(Curr)) { + // Storing a constant integer. + if (llvm::isa(Store->getValueOperand())) { + return psr::strongUpdateStore(Store, + psr::LLVMZeroValue::isLLVMZeroValue); + } + } + + // Leave everything else defaulted + return this->DefaultNoAliasIDEProblem::getNormalFlowFunction(Curr, Succ); + } + + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override { + // We definitely want to re-use as much as possible from the default + // call-flow-function + auto DefaultFn = + this->DefaultNoAliasIDEProblem::getCallFlowFunction(CallSite, DestFun); + + // If a constant int is passed as parameter, we need to generate the + // parameter inside the callee from zero + + const auto *Call = llvm::cast(CallSite); + container_type Gen; + for (const auto &[Arg, Param] : llvm::zip(Call->args(), DestFun->args())) { + if (llvm::isa(Arg)) { + Gen.insert(&Param); + } + } + + if (Gen.empty()) { + // Nothing special, we can directly use the default call-FF + return DefaultFn; + } + + // Here, we combine both flow-functions: + + auto GenFn = + generateManyFlowsAndKillAllOthers(std::move(Gen), getZeroValue()); + return unionFlows(std::move(DefaultFn), std::move(GenFn)); + } + + FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, + n_t ExitInst, n_t RetSite) override { + + auto DefaultFn = this->DefaultNoAliasIDEProblem::getRetFlowFunction( + CallSite, CalleeFun, ExitInst, RetSite); + + const auto *RetInst = llvm::dyn_cast(ExitInst); + if (RetInst && + llvm::isa_and_present(RetInst->getReturnValue())) { + // 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 + // LLVM's SSA form + + auto RetFn = generateFlowAndKillAllOthers(CallSite, getZeroValue()); + return unionFlows(std::move(DefaultFn), std::move(RetFn)); + } + + return DefaultFn; + } + + // Fallback edge-function that models two composed edge-functions. We try to + // use this as little as possible for performance reasons. + struct LCAEdgeFunctionComposer : psr::EdgeFunctionComposer { + static psr::EdgeFunction + join(psr::EdgeFunctionRef This, + const psr::EdgeFunction &OtherFunction) { + // Just use the default join. + + if (auto Default = defaultJoinOrNull(This, OtherFunction)) { + return Default; + } + return psr::AllBottom{}; + } + }; + + // The custom edge-function for binary operations + struct BinOp { + using l_t = ExampleIDELinearConstantAnalysisDomain::l_t; + + unsigned OpCode{}; + const llvm::ConstantInt *LeftConst{}; + const llvm::ConstantInt *RightConst{}; + + // Utility function to make implementing computeTarget() easier + [[nodiscard]] l_t executeBinOperation(l_t LVal, l_t RVal) const { + auto *LopPtr = LVal.getValueOrNull(); + auto *RopPtr = RVal.getValueOrNull(); + + if (!LopPtr || !RopPtr) { + return psr::Bottom{}; + } + + auto Lop = *LopPtr; + auto Rop = *RopPtr; + + // default initialize with BOTTOM (all information) + int64_t Res; + switch (OpCode) { + case llvm::Instruction::Add: + if (llvm::AddOverflow(Lop, Rop, Res)) { + return psr::Bottom{}; + } + return Res; + + case llvm::Instruction::Sub: + if (llvm::SubOverflow(Lop, Rop, Res)) { + return psr::Bottom{}; + } + return Res; + + case llvm::Instruction::Mul: + if (llvm::MulOverflow(Lop, Rop, Res)) { + return psr::Bottom{}; + } + return Res; + + case llvm::Instruction::UDiv: + case llvm::Instruction::SDiv: + if (Lop == std::numeric_limits::min() && + Rop == -1) { // Would produce and overflow, as the complement of min + // is not representable in a signed type. + return psr::Bottom{}; + } + if (Rop == 0) { // Division by zero is UB, so we return Bot + return psr::Bottom{}; + } + return Lop / Rop; + + case llvm::Instruction::URem: + case llvm::Instruction::SRem: + if (Rop == 0) { // Division by zero is UB, so we return Bot + return psr::Bottom{}; + } + return Lop % Rop; + + case llvm::Instruction::And: + return Lop & Rop; + case llvm::Instruction::Or: + return Lop | Rop; + case llvm::Instruction::Xor: + return Lop ^ Rop; + default: + PHASAR_LOG_LEVEL(DEBUG, "Operation not supported by " + "IDELinearConstantAnalysis::" + "executeBinOperation()"); + return psr::Bottom{}; + } + } + + // Utility function to aid the printing operator<< + static char opToChar(const unsigned Op) { + switch (Op) { + case llvm::Instruction::Add: + return '+'; + case llvm::Instruction::Sub: + return '-'; + case llvm::Instruction::Mul: + return '*'; + case llvm::Instruction::UDiv: + case llvm::Instruction::SDiv: + return '/'; + case llvm::Instruction::URem: + case llvm::Instruction::SRem: + return '%'; + case llvm::Instruction::And: + return '&'; + case llvm::Instruction::Or: + return '|'; + case llvm::Instruction::Xor: + return '^'; + default: + return ' '; + } + } + + // Required function that invokes the edge-function with an incoming value + // that is substituted with the non-constant operand of the modeled binary + // operation. + [[nodiscard]] l_t computeTarget(l_t Source) const { + if (LeftConst && RightConst) { // Simple constant-folding + return executeBinOperation(LeftConst->getSExtValue(), + RightConst->getSExtValue()); + } + if (Source == psr::Bottom{}) { + // Bottom is the top-value of our lattice. Whatever we do to it, it will + // always stay Bottom + return Source; + } + + // Now, perform the linear arithmetic. + // First, we have to check, which of the both operands is the literal + if (RightConst) { + // The right operand is the literal, so we plug in the incoming value as + // left operand + return executeBinOperation(Source, RightConst->getSExtValue()); + } + if (LeftConst) { + // The left operand is the literal, so we plug in the incoming value as + // right operand + return executeBinOperation(LeftConst->getSExtValue(), Source); + } + + llvm::report_fatal_error( + "Only linear constant propagation can be specified!"); + } + + // Optional function to expose the constant-ness of this edge-function for + // optimization purposes + [[nodiscard]] constexpr bool isConstant() const noexcept { + // If both operands of this binary operation are literal constant, this + // edge function always computes the same value + return LeftConst && RightConst; + } + + // Compose This edge-function with a different SecondFunction + static psr::EdgeFunction + compose(psr::EdgeFunctionRef This, + const psr::EdgeFunction &SecondFunction) { + // Trivial compositions can be defaulted: + if (auto Default = defaultComposeOrNull(This, SecondFunction)) { + return Default; + } + + // Here, we could for example add transformations like: + // compose(BinOp{Add, ..., Const1}, BinOp{Add, ..., Const2}) --> + // BinOp{Add, ..., Const1 + Const2} + + // Fallback for when we don't know better: + return LCAEdgeFunctionComposer{This, SecondFunction}; + } + + // Join This edge-function dith a different OtherFunction, going in the + // EF-lattice towards AllBottom. + static psr::EdgeFunction + join(psr::EdgeFunctionRef This, + const psr::EdgeFunction &OtherFunction) { + // Trivial joins can be defaulted: + if (auto Default = defaultJoinOrNull(This, OtherFunction)) { + return Default; + } + + // Here we could, e.g., check whether the two edge functions are + // semantically equivalent, althouth different and then return one of them + + // Sound fallback in case, we don't know better + return psr::AllBottom{}; + } + + constexpr bool operator==(const BinOp &Other) const noexcept { + return OpCode == Other.OpCode && LeftConst == Other.LeftConst && + RightConst == Other.RightConst; + } + + // Printing. optional + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const BinOp &Bop) { + OS << "BinOp["; + if (Bop.LeftConst) { + OS << *Bop.LeftConst; + } else { + OS << 'x'; + } + + OS << ' ' << opToChar(Bop.OpCode) << ' '; + if (Bop.LeftConst) { + OS << *Bop.LeftConst; + } else { + OS << 'x'; + } + + return OS; + } + }; + + psr::EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, + n_t /*Succ*/, + d_t SuccNode) override { + if (isZeroValue(CurrNode) && !isZeroValue(SuccNode)) { + // Handle the two cases, where we generate facts from zero: + + if (const auto *Alloca = llvm::dyn_cast(Curr)) { + // Freshly allocated variables hold no constant value + return psr::AllBottom{}; + } + + if (const auto *Store = llvm::dyn_cast(Curr)) { + + // Storing a constant integer. + const auto *ConstOperand = + llvm::cast(Store->getValueOperand()); + return psr::ConstantEdgeFunction{ConstOperand->getSExtValue()}; + } + } + + // Handle binary operations. The corresponding flow-function is defaulted. + if (llvm::isa(Curr) && SuccNode == Curr && + CurrNode != SuccNode) { + unsigned Op = Curr->getOpcode(); + auto *Lop = Curr->getOperand(0); + auto *Rop = Curr->getOperand(1); + // For non linear constant computation we propagate bottom + if ((CurrNode == Lop && !llvm::isa(Rop)) || + (CurrNode == Rop && !llvm::isa(Lop))) { + return psr::AllBottom{}; + } + + // Attach the arithmetic transformer to this edge + return BinOp{Op, llvm::dyn_cast(Lop), + llvm::dyn_cast(Rop)}; + } + + // Pass everything else as identity + return psr::EdgeIdentity{}; + } + + psr::EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t /*DestinationFunction*/, + d_t DestNode) override { + if (isZeroValue(SrcNode) && !isZeroValue(DestNode)) { + // If a constant int is passed as parameter, we need to generate the + // parameter inside the callee from zero + const auto *DestParam = llvm::cast(DestNode); + const auto *ConstOperand = llvm::cast( + CallSite->getOperand(DestParam->getArgNo())); + return psr::ConstantEdgeFunction{ConstOperand->getSExtValue()}; + } + + // Pass everything else as identity + 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 { + 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 + // LLVM's SSA form + const auto *RetVal = + llvm::cast(ExitStmt)->getReturnValue(); + return psr::ConstantEdgeFunction{ + llvm::cast(RetVal)->getSExtValue()}; + } + + // Pass everything else as identity + return psr::EdgeIdentity{}; + } + + psr::EdgeFunction + getCallToRetEdgeFunction(n_t /*CallSite*/, d_t /*CallNode*/, n_t /*RetSite*/, + d_t /*RetSiteNode*/, + llvm::ArrayRef /*Callees*/) override { + // The call-to-return edge-function handles facts that are not affected by + // the call. This is usually the identity function. + return psr::EdgeIdentity{}; + } +}; +} // namespace + +// Invoke the analysis the same way as explained in 05-run-ide-analysis: +int main(int Argc, char *Argv[]) { + if (Argc < 2) { + llvm::errs() << "USAGE: write-ide-analysis-simple \n"; + return 1; + } + + psr::LLVMProjectIRDB IRDB(Argv[1]); + if (!IRDB) { + return 1; + } + + psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"}); + + ExampleLinearConstantAnalysis LCAProblem(&IRDB, {"main"}); + + auto Results = psr::solveIDEProblem(LCAProblem, ICFG); + + // After we have solved the LCAProblem, we can now inspect the detected + // constants: + + const auto *MainF = IRDB.getFunctionDefinition("main"); + if (!MainF) { + llvm::errs() << "Required function 'main' not found\n"; + return 1; + } + + const auto *ExitOfMain = psr::getAllExitPoints(MainF).front(); + + // Get the analysis results right **after** main's return statement + const auto &AllConstantsAtMainExit = Results.resultsAt(ExitOfMain); + + llvm::outs() << "Detected constants at " << psr::llvmIRToString(ExitOfMain) + << ":\n"; + for (const auto &[LLVMVar, ConstVal] : AllConstantsAtMainExit) { + llvm::outs() << " " << psr::llvmIRToString(LLVMVar) << "\n --> "; + if (ConstVal.isBottom()) { + // A "bottom" value here means that the analysis does not know the value + // at this point and that any value may be possible. + + llvm::outs() << "\n\n"; + } else { + llvm::outs() << ConstVal << "\n\n"; + } + } +} diff --git a/examples/how-to/CMakeLists.txt b/examples/how-to/CMakeLists.txt new file mode 100644 index 0000000000..5a1ae5b094 --- /dev/null +++ b/examples/how-to/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(phasar-how-tos) + +file(GLOB children RELATIVE ${CMAKE_CURRENT_LIST_DIR} ${CMAKE_CURRENT_LIST_DIR}/*) + +add_custom_target(run_sample_programs) + +foreach(child ${children}) + if(IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/${child} AND NOT "${child}" STREQUAL "build") + add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/${child}) + endif() +endforeach() diff --git a/examples/how-to/README.md b/examples/how-to/README.md new file mode 100644 index 0000000000..b84875a609 --- /dev/null +++ b/examples/how-to/README.md @@ -0,0 +1,13 @@ +# How To ... + +This folder contains various examples on how to use certain features of PhASAR. + +Currently supporting: +- [x] Working with the IR ([here](./00-load-llvm-ir/README.md)) +- [x] Build a type-hierarchy ([here](./01-build-type-hierarchy/README.md)) +- [x] Build a call-graph ([here](./02-build-call-graph/README.md)) +- [x] Create alias-information ([here](./03-create-alias-info/README.md)) +- [x] Run an IFDS analysis ([here](./04-run-ifds-analysis/README.md)) +- [x] Run an IDE analysis ([here](./05-run-ide-analysis/README.md)) +- [x] Write an IFDS analysis ([here](./07-write-ifds-analysis/README.md)) +- [x] Write an IDE analysis ([here](./08-write-ide-analysis/)) diff --git a/examples/llvm-hello-world/CMakeLists.txt b/examples/llvm-hello-world/CMakeLists.txt new file mode 100644 index 0000000000..8ed9e80124 --- /dev/null +++ b/examples/llvm-hello-world/CMakeLists.txt @@ -0,0 +1,13 @@ +cmake_minimum_required(VERSION 3.14...3.28) + +project(llvm-hello-world) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +find_package(LLVM 15 REQUIRED CONFIG) + +add_executable(main main.cpp) +target_link_libraries(main PRIVATE LLVMCore LLVMIRReader) +target_include_directories(main PRIVATE ${LLVM_INCLUDE_DIRS}) + +add_subdirectory(target) diff --git a/examples/llvm-hello-world/README.md b/examples/llvm-hello-world/README.md new file mode 100644 index 0000000000..23a1eb7854 --- /dev/null +++ b/examples/llvm-hello-world/README.md @@ -0,0 +1,30 @@ +# LLVM Hello World + +The "Hello, World!" program can be compiled using: + +```bash +$ make +``` + +However, we recommend using cmake: + +```bash +$ mkdir -p build && cd build +$ cmake .. +$ cmake --build . +``` + +"Hello, World!" reads a LLVM IR file (.ll or .bc) specified by the first +command-line argument. It then looks for the main function, iterates all of its +instructions and prints them to the command-line using an LLVM output stream. +Have a look at the comments within the source code in main.cpp. + +Example use: + +```bash +# Invoked from the llvm-hello-world root folder if compiled with make: +./main ./target/simple.ll + +# Invoked from the llvm-hello-world/build folder if compiled with cmake: +./main ./target/simple_cpp_dbg.ll +``` diff --git a/examples/llvm-hello-world/README.txt b/examples/llvm-hello-world/README.txt deleted file mode 100644 index 51b3fcedf6..0000000000 --- a/examples/llvm-hello-world/README.txt +++ /dev/null @@ -1,12 +0,0 @@ -The "Hello, World!" program can be compiled using: - - $ make - -The auto-generated files can be removed using: - - $ make clean - -"Hello, World!" reads a LLVM IR file (.ll or .bc) specified by the first -command-line argument. It then looks for the main function, iterates all of its -instructions and prints them to the command-line using an LLVM output stream. -Have a look at the comments within the source code in main.cpp. diff --git a/examples/llvm-hello-world/main.cpp b/examples/llvm-hello-world/main.cpp index 3cb439b218..ce435a7de2 100644 --- a/examples/llvm-hello-world/main.cpp +++ b/examples/llvm-hello-world/main.cpp @@ -2,18 +2,14 @@ #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" -#include "llvm/IR/IntrinsicInst.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" #include -#include int main(int argc, char **argv) { if (argc != 2) { diff --git a/examples/llvm-hello-world/target/CMakeLists.txt b/examples/llvm-hello-world/target/CMakeLists.txt new file mode 100644 index 0000000000..f8f25bef38 --- /dev/null +++ b/examples/llvm-hello-world/target/CMakeLists.txt @@ -0,0 +1,12 @@ +add_custom_target(LLFileGeneration ALL) + +# Use phasar's capabilities to automate the LLVM-IR file generation + +include(../../../cmake/phasar_macros.cmake) +set(PHASAR_LLVM_VERSION 15) + +file(GLOB target_files RELATIVE ${CMAKE_CURRENT_LIST_DIR} *.cpp) + +foreach(target_file ${target_files}) + generate_ll_file(FILE ${target_file} DEBUG) +endforeach() diff --git a/examples/llvm-hello-world/target/branching.ll b/examples/llvm-hello-world/target/branching.ll index cc4c36efa6..098eee04f0 100644 --- a/examples/llvm-hello-world/target/branching.ll +++ b/examples/llvm-hello-world/target/branching.ll @@ -1,43 +1,89 @@ ; ModuleID = 'branching.cpp' source_filename = "branching.cpp" -target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" -; Function Attrs: noinline norecurse nounwind optnone uwtable -define i32 @main(i32, i8**) #0 { - %3 = alloca i32, align 4 - %4 = alloca i32, align 4 - %5 = alloca i8**, align 8 - %6 = alloca i32, align 4 - %7 = alloca i32, align 4 - store i32 0, i32* %3, align 4 - store i32 %0, i32* %4, align 4 - store i8** %1, i8*** %5, align 8 - store i32 10, i32* %6, align 4 - %8 = load i32, i32* %4, align 4 - %9 = sub nsw i32 %8, 1 - %10 = icmp ne i32 %9, 0 - br i1 %10, label %11, label %12 - -;