diff --git a/.clang-tidy b/.clang-tidy index 93ad42fe9..54f04105c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -4,6 +4,7 @@ Checks: '-*, misc-*, -misc-non-private-member-variables-in-classes, -misc-no-recursion, + -misc-use-anonymous-namespace, readability-*, -readability-function-cognitive-complexity, -readability-else-after*, @@ -25,12 +26,13 @@ Checks: '-*, -cppcoreguidelines-non-private-member-variables-in-classes, -cppcoreguidelines-init-variables, -cppcoreguidelines-macro-usage, + -cppcoreguidelines-avoid-do-while, bugprone-*, -bugprone-easily-swappable-parameters, modernize-*, -modernize-use-trailing-return-type, performance-*, - clang-analyzer-*, + clang-analyzer-* ' FormatStyle: LLVM @@ -55,7 +57,7 @@ CheckOptions: - key: readability-identifier-naming.ParameterIgnoredRegexp value: (d|d1|d2|d3|d4|d5|eP|f|n) - key: readability-identifier-naming.FunctionIgnoredRegexp - value: (try_emplace|from_json|to_json|equal_to|to_string) + value: (try_emplace|from_json|to_json|equal_to|to_string|DToString|NToString|FToString|LToString) - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor value: 1 - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index bfd339c16..3e7412921 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -26,3 +26,9 @@ Dockerfile @janniclas /.docker/ @janniclas /include/phasar/Utils/Logger.h @MMory +/include/phasar/Utils/AnalysisPrinterBase.h @sritejakv +/include/phasar/Utils/DefaultAnalysisPrinter.h @sritejakv +/include/phasar/Utils/NullAnalysisPrinter.h @sritejakv +/include/phasar/Utils/OnTheFlyAnalysisPrinter.h @sritejakv +/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h @sritejakv +/lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp @sritejakv diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index a571a74a2..24e0ca0b9 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -11,7 +11,7 @@ assignees: '' Replace the empty checkboxes [ ] below with checked ones [x] accordingly. --> - [ ] I have searched open and closed issues for duplicates - - [ ] I made sure that I am not using an old project version (DO: pull Phasar, update git submodules, rebuild the project and check if the bug is still there) + - [ ] I made sure that I am not using an old project version (DO: pull PhASAR, update git submodules, rebuild the project and check if the bug is still there) ---------------------------------------- @@ -24,9 +24,9 @@ Describe here the issue that you are experiencing. - that reproduce the bug - e.g. cli arguments and flags -**Actual result:** Describe here what happens after you run the steps above (i.e. the buggy behaviour) +**Actual result:** Describe here what happens after you run the steps above (i.e. the buggy behavior) -**Expected result:** Describe here what should happen after you run the steps above (i.e. what would be the correct behaviour) +**Expected result:** Describe here what should happen after you run the steps above (i.e. what would be the correct behavior) ### Context (Environment) @@ -35,7 +35,7 @@ Describe here the issue that you are experiencing. - **phasar:** \[commit-id] - **googletest:** \[commit-id] - **json:** \[commit-id] - - **WALi-OpenNWA:** \[commit-id] + - **json-schema-validator** \[commit-id] @@ -48,11 +48,12 @@ Describe here the issue that you are experiencing. **Build Type:** - [ ] cmake + - [ ] bootstrap.sh - [ ] custom build ### Possible solution -We are happy to discuss possible solutions to this problem, especially if it origniates from a design flaw. +We are happy to discuss possible solutions to this problem, especially if it originates from a design flaw. ### Example files diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e4c32f0b..b8dab4ed4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,29 +14,25 @@ jobs: matrix: compiler: [ [clang++-14, clang-14] ] build: [ Debug, Release ] + include: + - build: Debug + flags: -DPHASAR_BUILD_DYNLIB=ON -DPHASAR_ENABLE_SANITIZERS=ON + - build: Release + flags: -DPHASAR_ENABLE_DYNAMIC_LOG=OFF -DPHASAR_DEBUG_LIBDEPS=ON -DBUILD_SHARED_LIBS=ON continue-on-error: false steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: fetch-depth: 0 submodules: recursive - - name: Install Basic Dependencies - shell: bash - run: | - sudo apt-get update - sudo apt-get -y install --no-install-recommends \ - cmake \ - ninja-build \ - libstdc++6 \ - libboost-all-dev - - name: Install Phasar Dependencies shell: bash run: | ./utils/InstallAptDependencies.sh + sudo apt-get -y install --no-install-recommends libboost-graph-dev - name: Install Strategy Dependencies shell: bash @@ -55,9 +51,10 @@ jobs: libclang-rt-14-dev - uses: swift-actions/setup-swift@v1 + with: + swift-version: "5.8.1" - name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }} env: - BUILD_TYPE: ${{ matrix.build }} CXX: ${{ matrix.compiler[0] }} CC: ${{ matrix.compiler[1] }} shell: bash @@ -65,9 +62,10 @@ jobs: mkdir build cd build cmake .. \ - -DCMAKE_BUILD_TYPE=$BUILD_TYPE \ - -DCMAKE_CXX_COMPILER=$CXX \ - -DBUILD_SWIFT_TESTS=1 \ + -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ + -DBUILD_SWIFT_TESTS=ON \ + -DPHASAR_USE_Z3=ON \ + ${{ matrix.flags }} \ -G Ninja cmake --build . diff --git a/.gitignore b/.gitignore index f175263c3..1cc71e473 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ bin/* # build directories for cmake build/ build_*/ +build-*/ # LLVM project llvm-project/* diff --git a/BreakingChanges.md b/BreakingChanges.md index 2d73d94c6..027768f00 100644 --- a/BreakingChanges.md +++ b/BreakingChanges.md @@ -1,29 +1,35 @@ # Breaking Changes +## v2403 + +- Versioning scheme has been changed from `` to `` +- Default build mode is no longer `SHARED` but `STATIC`. To build in shared mode, use the cmake option `BUILD_SHARED_LIBS` which we don't recommend anymore. Consider using `PHASAR_BUILD_DYNLIB` instead to build one big libphasar.so. +- Build type `DebugSan` has been removed in favor of a new CMake option `PHASAR_ENABLE_SANITIZERS` that not only works in `Debug` mode. + ## v0323 - `EdgeFunctionPtrType` is no longer a `std::shared_ptr`. Instead `EdgeFunction` should be used directly. `EdgeFunction` is now a *value-type* that encapsulates its memory management by itself. - Concrete `EdgeFunction` types no longer derive from any base-class. Instead they just need to implement the required API functions. `EdgeFunction` implementations should me move-constructible and can be implicitly cast to `EdgeFunction`. To verify that your type implements the edge function interface use the `IsEdgeFunction` type trait. The API functions have been changed as follows: - - All API functions of `EdgeFunction` must be `const` qualified. - - `EdgeFunctionPtrType composeWith(EdgeFunctionPtrType SecondFunction)` and `EdgeFunctionPtrType joinWith(EdgeFunctionPtrType OtherFunction)` have been changed to `static EdgeFunction compose(EdgeFunctionRef This, const EdgeFunction& SecondFunction)` and `static EdgeFunction join(EdgeFunctionRef This, const EdgeFunction& OtherFunction)` respectively. Here, the `This` parameter models the former `shared_from_this()`. - - `bool equal_to(EdgeFunctionPtrType Other)const` has been changed to `bool operator==(const T &Other)const noexcept`, where `T` is your concrete edge function type. - - `void print(llvm::raw_ostream &OS, bool IsForDebug)` has been changed to `friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const T& EF)`. + - All API functions of `EdgeFunction` must be `const` qualified. + - `EdgeFunctionPtrType composeWith(EdgeFunctionPtrType SecondFunction)` and `EdgeFunctionPtrType joinWith(EdgeFunctionPtrType OtherFunction)` have been changed to `static EdgeFunction compose(EdgeFunctionRef This, const EdgeFunction& SecondFunction)` and `static EdgeFunction join(EdgeFunctionRef This, const EdgeFunction& OtherFunction)` respectively. Here, the `This` parameter models the former `shared_from_this()`. + - `bool equal_to(EdgeFunctionPtrType Other)const` has been changed to `bool operator==(const T &Other)const noexcept`, where `T` is your concrete edge function type. + - `void print(llvm::raw_ostream &OS, bool IsForDebug)` has been changed to `friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const T& EF)`. - `EdgeFunction` is tagged with `[[clang::trivial_abi]]`. Hence, you should not rely on any destruction order within a top-level statement that uses temporary `EdgeFunction` objects. - `EdgeFunctionSingletonFactory` has been removed. Use `EdgeFunctionSingletonCache` instead. - `TaintConfig` has been renamed to `LLVMTaintConfig`. For generic code you may want to use the LLVM-independent `TaintConfigBase` CRTP interface instead. - Renamed `phasar/PhasarLLVM/DataFlowSolver/` to either `phasar/DataFlow/` or `phasar/PhasarLLVM/DataFlow/` depending on whether the components need LLVMCore. Analoguous changes in `lib/` and `unittests/`. An incomplete list of moved/renamed files: - - `phasar/PhasarLLVM/DataFlowSolver/IfdsIde/Solver/*` => `phasar/DataFlow/IfdsIde/Solver/*` - - `phasar/PhasarLLVM/DataFlowSolver/IfdsIde/IDETabulationProblem.h` => `phasar/DataFlow/IfdsIde/IDETabulationProblem.h` - - `phasar/DB/LLVMProjectIRDB.h` => `phasar/PhasarLLVM/DB/LLVMProjectIRDB.h` - - ... + - `phasar/PhasarLLVM/DataFlowSolver/IfdsIde/Solver/*` => `phasar/DataFlow/IfdsIde/Solver/*` + - `phasar/PhasarLLVM/DataFlowSolver/IfdsIde/IDETabulationProblem.h` => `phasar/DataFlow/IfdsIde/IDETabulationProblem.h` + - `phasar/DB/LLVMProjectIRDB.h` => `phasar/PhasarLLVM/DB/LLVMProjectIRDB.h` + - ... - Renamed and split up some libraries: - - `phasar_phasarllvm_utils` => `phasar_llvm_utils` - - `phasar_typehierarchy` => `phasar_llvm_typehierarchy` - - `phasar_ifdside` => `phasar_llvm_ifdside` - - `phasar_controlflow` has its LLVM dependent stuff moved to `phasar_llvm_controlflow` - - `phasar_db` has its LLVM dependent stuff moved to `phasar_llvm_db` - - `phasar_pointer` has its LLVM dependent stuff moved to `phasar_llvm_pointer` + - `phasar_phasarllvm_utils` => `phasar_llvm_utils` + - `phasar_typehierarchy` => `phasar_llvm_typehierarchy` + - `phasar_ifdside` => `phasar_llvm_ifdside` + - `phasar_controlflow` has its LLVM dependent stuff moved to `phasar_llvm_controlflow` + - `phasar_db` has its LLVM dependent stuff moved to `phasar_llvm_db` + - `phasar_pointer` has its LLVM dependent stuff moved to `phasar_llvm_pointer` - Renamed the phasar tool `phasar-llvm` to `phasar-cli` - `LLVMPointsTo[.*]` has been renamed to `LLVMAlias[.*]` - The ctor of `LLVMAliasSet` now takes the `LLVMProjectIRDB` as pointer instead of a reference to better document that it may capture the IRDB by reference. diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f80a1ca2..2ddf030ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,16 @@ -cmake_minimum_required (VERSION 3.0) +cmake_minimum_required (VERSION 3.14) + +# Avoid IPO/LTO Warnings: +cmake_policy(SET CMP0069 NEW) +set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) + +# Allow overwriting options of external projects from this CMakeLists file +cmake_policy(SET CMP0077 NEW) +set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + +# Allow portable use of CMAKE_VISIBILITY_INLINES_HIDDEN not only for shared libraries +cmake_policy(SET CMP0063 NEW) +set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) # Check if we build within the llvm source tree if (DEFINED LLVM_MAIN_SRC_DIR) @@ -10,8 +22,15 @@ if (NOT PHASAR_IN_TREE) set(CMAKE_PROJECT_NAME "phasar") endif () +option(PHASAR_EXPERIMENTAL_CXX20 "Build phasar in C++20 mode. This is an experimental feature" OFF) + set(CMAKE_EXPORT_COMPILE_COMMANDS YES) -set(CMAKE_CXX_STANDARD 17) +if(PHASAR_EXPERIMENTAL_CXX20) + message(STATUS "Selected experimental C++20 build") + set(CMAKE_CXX_STANDARD 20) +else() + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -19,101 +38,176 @@ include(GNUInstallDirs) set_property(GLOBAL PROPERTY TARGET_MESSAGES OFF) -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build mode ('DebugSan' or 'Debug' or 'Release', default is 'Debug')" FORCE) +option(PHASAR_ENABLE_SANITIZERS "Build PhASAR with AddressSanitizer and UBSanitizer (default is OFF)" OFF) + +set(PHASAR_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(PHASAR_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PHASAR_SRC_DIR}/cmake") +include("phasar_macros") + +if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG) + message(STATUS "No CMAKE_BUILD_TYPE specified, setting it to Debug") + set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Build mode ('Debug' or 'Release', default is 'Debug')" FORCE) endif () -if(CMAKE_BUILD_TYPE STREQUAL "DebugSan") - message(STATUS "Selected Debug Build with sanitizers") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MP -fvisibility-inlines-hidden -fstack-protector-strong -ffunction-sections -fdata-sections -pipe -g -fno-omit-frame-pointer -fsanitize=address,undefined") -elseif(CMAKE_BUILD_TYPE STREQUAL "Debug") - message(STATUS "Selected Debug Build") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MP -fvisibility-inlines-hidden -fstack-protector-strong -ffunction-sections -fdata-sections -pipe -g") +if(GENERATOR_IS_MULTI_CONFIG) + message(STATUS "Selected multi-config Build") + set(CMAKE_CONFIGURATION_TYPES Debug RelWithDebInfo Release CACHE STRING "Configuration types: Debug, RelWithDebInfo and Release" FORCE) else() - message(STATUS "Selected Release Build") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -MP -fvisibility-inlines-hidden -fstack-protector-strong -ffunction-sections -fdata-sections -pipe") + message(STATUS "Selected ${CMAKE_BUILD_TYPE} Build") endif() -# Enable testing -enable_testing() +set(DEBUG_CONFIGURATIONS DEBUG CACHE INTERNAL "" FORCE) +set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE) -# TODO: allow Phasar to be build as a llvm drop-in as well -# if (NOT DEFINED LLVM_MAIN_SRC_DIR) -# message(FATAL_ERROR "Phasar is not a llvm drop-in, abort!") -# endif() +# TODO: Once available, we may want to use -fextend-lifetimes on Debug- and RelWithDebInfo builds to improve debugging experience +# https://reviews.llvm.org/D157613 -set(PHASAR_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) -set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PHASAR_SRC_DIR}/cmake") +string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe") +string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og -fno-omit-frame-pointer") +string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer") +string(APPEND CMAKE_CXX_FLAGS_RELEASE "") -file(STRINGS ${PHASAR_SRC_DIR}/include/phasar/Config/Version.h VERSION_NUMBER_FILE) -string(REPLACE " " ";" VERSION_NUMBER_FILE ${VERSION_NUMBER_FILE}) -list(GET VERSION_NUMBER_FILE 2 VERSION_NUMBER_PHASAR) +option(CMAKE_VISIBILITY_INLINES_HIDDEN "Hide inlined functions from the DSO table (default ON)" ON) -include("phasar_macros") +include(CheckCXXCompilerFlag) -option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ON) +# Handle memory issues with linking +if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + string(APPEND CMAKE_CXX_FLAGS_DEBUG " -gsplit-dwarf") + string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -gsplit-dwarf") + set(LINKER_FLAGS_SAVE ${CMAKE_EXE_LINKER_FLAGS}) -option(BUILD_SWIFT_TESTS "Builds the Swift tests (Swift compiler has to be installed manually beforehand!)" OFF) -if (BUILD_SWIFT_TESTS) - set(CMAKE_Swift_FLAGS_RELEASE "-g") - set(CMAKE_Swift_FLAGS_RELWITHDEBINFO "-g") - enable_language(Swift) -endif(BUILD_SWIFT_TESTS) + # See LLVM_USE_SPLIT_DWARF in LLVM + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,--gdb-index") + cmake_policy(PUSH) + cmake_policy(SET CMP0056 NEW) + check_cxx_compiler_flag("" GDB_INDEX_SUPPORTED) + cmake_policy(POP) + set(CMAKE_EXE_LINKER_FLAGS ${LINKER_FLAGS_SAVE}) + + if(GDB_INDEX_SUPPORTED) + link_libraries(debug "-Wl,--gdb-index") + endif() +endif() + +# march=native + +# NOTE: Use gcc -march=native -Q --help=target | grep -- '-march=' | cut -f3 +# to check the architecture detected by match=native +# set(PHASAR_TARGET_ARCH "" CACHE STRING "Optimize the build for the given target architecture, e.g. -march=native. Most useful in Release builds. Disabled by default") + +if (DEFINED PHASAR_TARGET_ARCH) + if (NOT CMAKE_BUILD_TYPE STREQUAL "Release" AND NOT GENERATOR_IS_MULTI_CONFIG) + message(WARNING "The PHASAR_TARGET_ARCH flag will be ignored in non-Release build type ${CMAKE_BUILD_TYPE}") + else() + set(PHASAR_TARGET_ARCH_INTERNAL "${PHASAR_TARGET_ARCH}") + endif() +elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Release") + set(PHASAR_TARGET_ARCH_INTERNAL "native") +endif() + +if (NOT "${PHASAR_TARGET_ARCH_INTERNAL}" STREQUAL "") + check_cxx_compiler_flag("-march=${PHASAR_TARGET_ARCH_INTERNAL}" MARCH_SUPPORTED) + if (MARCH_SUPPORTED) + message(STATUS "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' enabled") + string(APPEND CMAKE_CXX_FLAGS_RELEASE " -march=${PHASAR_TARGET_ARCH_INTERNAL}") + else() + message(WARNING "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' not supported. Fallback to generic build") + endif() +endif() + +# Sanitizers +if (PHASAR_ENABLE_SANITIZERS) + message(STATUS "Selected ${CMAKE_BUILD_TYPE} Build with Sanitizers") + + if(MSVC) + set(ASAN_FLAG "/fsanitize=address") + else() + set(ASAN_FLAG "-fsanitize=address,undefined") + endif() + + string(APPEND CMAKE_CXX_FLAGS " ${ASAN_FLAG}") +endif() + +# LTO +if (GENERATOR_IS_MULTI_CONFIG OR CMAKE_BUILD_TYPE STREQUAL "Release") + include(CheckIPOSupported) + check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_SUPPORT_ERROR) + + if(LTO_SUPPORTED) + message(STATUS "IPO/LTO enabled in Release mode") + set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE ON) # LTO + else() + message(STATUS "IPO/LTO not supported: ${LTO_SUPPORT_ERROR}") + endif() +endif() + +# Enable testing +enable_testing() + +option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ON) option(PHASAR_BUILD_OPENSSL_TS_UNITTESTS "Build OPENSSL typestate tests (require OpenSSL, default is OFF)" OFF) +option(PHASAR_USE_Z3 "Build the phasar_llvm_pathsensitivity library with Z3 support for constraint solving (default is OFF)" OFF) + option(PHASAR_BUILD_IR "Build IR test code (default is ON)" ON) option(PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD "Run clang-tidy during build (default is OFF)" OFF) option(PHASAR_BUILD_DOC "Build documentation" OFF) -option(PHASAR_DEBUG_LIBDEPS "Debug internal library dependencies (private linkage)" OFF) +option(PHASAR_BUILD_DYNLIB "Build one fat shared library. Requires BUILD_SHARED_LIBS to be turned OFF (default is OFF)" OFF) -option(BUILD_SHARED_LIBS "Build shared libraries (default is ON)" ON) +if(PHASAR_BUILD_DYNLIB AND BUILD_SHARED_LIBS) + message(FATAL_ERROR "PHASAR_BUILD_DYNLIB is incompatible with BUILD_SHARED_LIBS") +endif() -option(PHASAR_ENABLE_WARNINGS "Enable warnings" ON) -if (PHASAR_ENABLE_WARNINGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wno-unused-variable -Wno-unused-parameter -Wno-return-type-c-linkage ") -endif (PHASAR_ENABLE_WARNINGS) +option(PHASAR_DEBUG_LIBDEPS "Debug internal library dependencies (private linkage)" OFF) +if (PHASAR_DEBUG_LIBDEPS) + if (NOT BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) + message("PHASAR_DEBUG_LIBDEPS only works for shared libraries, so set BUILD_SHARED_LIBS=ON") + endif() + if (PHASAR_ENABLE_SANITIZERS) + message(FATAL_ERROR "PHASAR_DEBUG_LIBDEPS is incompatible with ASAN (see https://clang.llvm.org/docs/AddressSanitizer.html#usage)") + endif() +endif () option(PHASAR_ENABLE_PIC "Build Position-Independed Code" ON) if (PHASAR_ENABLE_PIC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) endif (PHASAR_ENABLE_PIC) +# PAMM if (NOT PHASAR_ENABLE_PAMM) set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')" FORCE) set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full") endif() if(PHASAR_ENABLE_PAMM STREQUAL "Core" AND NOT PHASAR_BUILD_UNITTESTS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPAMM_CORE") - message("PAMM metric severity level: Core") + set(PAMM_CORE ON) + message(STATUS "PAMM metric severity level: Core") elseif(PHASAR_ENABLE_PAMM STREQUAL "Full" AND NOT PHASAR_BUILD_UNITTESTS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DPAMM_FULL") - message("PAMM metric severity level: Full") -elseif(PHASAR_BUILD_UNITTESTS) - message("PAMM metric severity level: Off (due to unittests)") + set(PAMM_FULL ON) + message(STATUS "PAMM metric severity level: Full") +elseif(PHASAR_BUILD_UNITTESTS AND (PHASAR_ENABLE_PAMM STREQUAL "Core" OR PHASAR_ENABLE_PAMM STREQUAL "Full")) + message(WARNING "PAMM metric severity level: Off (due to unittests)") else() - message("PAMM metric severity level: Off") + message(STATUS "PAMM metric severity level: Off") endif() +# Logger option(PHASAR_ENABLE_DYNAMIC_LOG "Makes it possible to switch the logger on and off at runtime (default is ON)" ON) if (PHASAR_ENABLE_DYNAMIC_LOG) message(STATUS "Dynamic log enabled") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DDYNAMIC_LOG") + set(DYNAMIC_LOG ON) else() message(STATUS "Dynamic log disabled") endif() -configure_file(config.h.in config.h @ONLY) -include_directories(${CMAKE_CURRENT_BINARY_DIR}) - -include_directories( - ${PHASAR_SRC_DIR}/include -) - +# RPATH if (NOT PHASAR_IN_TREE) set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) @@ -125,12 +219,14 @@ if (NOT PHASAR_IN_TREE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) endif() +# Filesystem if (LLVM_ENABLE_LIBCXX) set(PHASAR_STD_FILESYSTEM c++fs) else() set(PHASAR_STD_FILESYSTEM stdc++fs) endif() +# Config set(PHASAR_CUSTOM_CONFIG_INSTALL_DIR "" CACHE STRING "If set, customizes the directory, where configuration files for PhASAR are installed (default is /usr/local/.phasar-config)") if ("${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}" STREQUAL "") set(PHASAR_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/.phasar-config/") @@ -138,22 +234,33 @@ else() set(PHASAR_CONFIG_INSTALL_DIR "${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}") endif() -add_compile_definitions(PHASAR_CONFIG_DIR="${PHASAR_CONFIG_INSTALL_DIR}") -add_compile_definitions(PHASAR_DIR="${PHASAR_SRC_DIR}") +# Headers + +add_library(phasar_interface INTERFACE) +target_include_directories(phasar_interface + INTERFACE + $ # The regular include folder + $ # The location of phasar-config.h + $ # The installed include folder +) ### Adding external libraries + # Threads find_package(Threads) # Boost -find_package(Boost 1.65.1 COMPONENTS graph ${BOOST_THREAD} REQUIRED) -#find_package(Boost 1.72.0 COMPONENTS graph ${BOOST_THREAD} REQUIRED) -include_directories(${Boost_INCLUDE_DIRS}) +find_package(Boost 1.65.1 COMPONENTS graph REQUIRED) # Disable clang-tidy for the external projects set(CMAKE_CXX_CLANG_TIDY "") +# Nlohmann JSON +set(JSON_BuildTests OFF) +set(JSON_Install ON) +add_subdirectory(external/json) + # We need to work around the behavior of nlohmann_json_schema_validator and nlohmann_json here # The validator needs the json part, but if you include it, the library of nlohmann_json_schema_validator # is not installed, leading to linker error. But just including nlohmann_json is not sufficient, as @@ -162,108 +269,147 @@ set(CMAKE_CXX_CLANG_TIDY "") # The following workaround may collapse or become unnecessary once the issue is # changed or fixed in nlohmann_json_schema_validator. -#Override option of nlohmann_json_schema_validator to not build its tests +# Override option of nlohmann_json_schema_validator to not build its tests set(BUILD_TESTS OFF CACHE BOOL "Build json-schema-validator-tests") -# Make nlohmann_json_schema_validator happy by telling it how to find the single include of nlohmann_json -include_directories(SYSTEM external/json/single_include/) - if (PHASAR_IN_TREE) set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS nlohmann_json_schema_validator) -endif() -set(nlohmann_json_DIR ${PHASAR_SRC_DIR}/external/json/single_include/) + set (PHASAR_USE_Z3 OFF) +endif() # Json Schema Validator set(JSON_VALIDATOR_INSTALL ON) add_subdirectory(external/json-schema-validator) -include_directories(external/json-schema-validator/src/) - -# now we finally add the subdirectory -set(JSON_BuildTests OFF) -set(JSON_Install ON) -add_subdirectory(external/json) # Googletest if (NOT PHASAR_IN_TREE) + set(BUILD_GMOCK OFF) + set(INSTALL_GTEST OFF) add_subdirectory(external/googletest EXCLUDE_FROM_ALL) - include_directories(external/googletest/googletest/include) - include_directories(external/googletest/googlemock/include) + set(GTEST_INCLUDE_DIR "external/googletest/googletest/include") else() # Set llvm distributed includes for gtest header - include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include) - include_directories(${LLVM_MAIN_SRC_DIR}/utils/unittest/googlemock/include) + set(GTEST_INCLUDE_DIR "${LLVM_MAIN_SRC_DIR}/utils/unittest/googletest/include") endif() # SQL find_path(SQLITE3_INCLUDE_DIR NAMES sqlite3.h) find_library(SQLITE3_LIBRARY NAMES sqlite3) -include_directories(${SQLITE3_INCLUDE_DIR}) + +option(USE_LLVM_FAT_LIB "Link against libLLVM.so instead of the individual LLVM libraries if possible (default is OFF; always on if BUILD_SHARED_LIBS is ON)" OFF) # LLVM if (NOT PHASAR_IN_TREE) # Only search for LLVM if we build out of tree find_package(LLVM 14 REQUIRED CONFIG) - include_directories(${LLVM_INCLUDE_DIRS}) - link_directories(${LLVM_LIB_PATH} ${LLVM_LIBRARY_DIRS}) -endif() - -add_definitions(${LLVM_DEFINITIONS}) - -if (NOT PHASAR_IN_TREE) find_library(LLVM_LIBRARY NAMES LLVM PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) - if(NOT ${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") - message(STATUS "Found consolidated shared LLVM lib " ${LLVM_LIBRARY} " that will be linked against.") - set(USE_LLVM_FAT_LIB on) + + if(USE_LLVM_FAT_LIB AND ${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") + message(WARNING "Did not find requested libLLVM.so. Link against individual modules instead") + set(USE_LLVM_FAT_LIB OFF) + elseif(BUILD_SHARED_LIBS AND NOT ${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") + message(STATUS "Found consolidated shared LLVM lib ${LLVM_LIBRARY} that will be linked against.") + set(USE_LLVM_FAT_LIB ON) endif() + + if (NOT USE_LLVM_FAT_LIB) + message(STATUS "Link against individual LLVM modules") + set(LLVM_REQUIRED_LIBRARIES + Core + Support + BitWriter + Analysis + Passes + Demangle + Analysis + IRReader + Linker + ) + foreach(lib ${LLVM_REQUIRED_LIBRARIES}) + find_library(LLVM_SMALL_LIB${lib} NAMES LLVM${lib} PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) + if(LLVM_SMALL_LIB${lib} MATCHES "NOTFOUND$") + list(APPEND LLVM_SMALL_LIB_NOTFOUND "LLVM${lib}") + endif() + endforeach() + + if(DEFINED LLVM_SMALL_LIB_NOTFOUND) + if(${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") + message(FATAL_ERROR "Did not find a complete version of LLVM: Did not find the fat lib libLLVM.so, but also did not find the individual modules ${LLVM_SMALL_LIB_NOTFOUND}.") + else() + set(USE_LLVM_FAT_LIB ON) + list(JOIN LLVM_SMALL_LIB_NOTFOUND ", " LLVM_SMALL_LIB_NOTFOUND_PRETTY) + message(WARNING "Did not find the LLVM modules ${LLVM_SMALL_LIB_NOTFOUND_PRETTY}. Fallback to link against ${LLVM_LIBRARY}. To silence this warning, set -DUSE_LLVM_FAT_LIB=ON in the cmake invocation.") + endif() + endif(DEFINED LLVM_SMALL_LIB_NOTFOUND) + endif(NOT USE_LLVM_FAT_LIB) +endif(NOT PHASAR_IN_TREE) + +if(NOT LLVM_ENABLE_RTTI AND NOT PHASAR_IN_TREE) + message(FATAL_ERROR "PhASAR requires a LLVM version that is built with RTTI") endif() +# Z3 Solver +if(PHASAR_USE_Z3) + # This z3-version is the same version LLVM requires; however, we cannot just use Z3 via the LLVM interface + # as it lacks some functionality (such as z3::expr::simplify()) that we require + find_package(Z3 4.7.1 REQUIRED) + + if(NOT TARGET z3) + add_library(z3 IMPORTED SHARED) + set_property(TARGET z3 PROPERTY + IMPORTED_LOCATION ${Z3_LIBRARIES}) + set_property(TARGET z3 PROPERTY + INTERFACE_INCLUDE_DIRECTORIES ${Z3_INCLUDE_DIR}) + endif() +endif(PHASAR_USE_Z3) + # Clang -# The clang-cpp shared library is now the preferred way to link dynamically against libclang if we build out of tree. -if(NOT PHASAR_IN_TREE) - find_library(CLANG_LIBRARY NAMES clang-cpp libclang-cpp REQUIRED HINTS ${LLVM_LIBRARY_DIRS}) - if(${CLANG_LIBRARY} STREQUAL "CLANG_LIBRARY-NOTFOUND") - set(NEED_LIBCLANG_COMPONENT_LIBS on) +option(BUILD_PHASAR_CLANG "Build the phasar_clang library (default is ON)" ON) + +if(BUILD_PHASAR_CLANG) + # The clang-cpp shared library is now the preferred way to link dynamically against libclang if we build out of tree. + if(NOT PHASAR_IN_TREE) + find_library(CLANG_LIBRARY NAMES clang-cpp libclang-cpp HINTS ${LLVM_LIBRARY_DIRS}) + if(${CLANG_LIBRARY} STREQUAL "CLANG_LIBRARY-NOTFOUND") + set(NEED_LIBCLANG_COMPONENT_LIBS on) + endif() + endif() + # As fallback, look for the small clang libraries + if(PHASAR_IN_TREE OR NEED_LIBCLANG_COMPONENT_LIBS) + set(CLANG_LIBRARY + clangTooling + clangFrontendTool + clangFrontend + clangDriver + clangSerialization + clangCodeGen + clangParse + clangSema + clangStaticAnalyzerFrontend + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore + clangAnalysis + clangARCMigrate + clangRewrite + clangRewriteFrontend + clangEdit + clangAST + clangASTMatchers + clangLex + clangBasic + LLVMFrontendOpenMP) endif() -endif() -# As fallback, look for the small clang libraries -if(PHASAR_IN_TREE OR NEED_LIBCLANG_COMPONENT_LIBS) - set(CLANG_LIBRARY - clangTooling - clangFrontendTool - clangFrontend - clangDriver - clangSerialization - clangCodeGen - clangParse - clangSema - clangStaticAnalyzerFrontend - clangStaticAnalyzerCheckers - clangStaticAnalyzerCore - clangAnalysis - clangARCMigrate - clangRewrite - clangRewriteFrontend - clangEdit - clangAST - clangASTMatchers - clangLex - clangBasic - LLVMFrontendOpenMP) -endif() -if (NOT PHASAR_IN_TREE) - # Only search for clang if we build out of tree - link_directories(${CLANG_LIB_PATH}) -endif() -if (PHASAR_IN_TREE) - # Phasar needs clang headers, specificaly some that are generated by clangs table-gen - include_directories( - ${CLANG_INCLUDE_DIR} - ${PHASAR_SRC_DIR}/../clang/include - ${PROJECT_BINARY_DIR}/tools/clang/include - ) -endif() + if (PHASAR_IN_TREE) + # Phasar needs clang headers, specificaly some that are generated by clangs table-gen + include_directories( + ${CLANG_INCLUDE_DIR} + ${PHASAR_SRC_DIR}/../clang/include + ${PROJECT_BINARY_DIR}/tools/clang/include + ) + endif() +endif(BUILD_PHASAR_CLANG) # Set up clang-tidy to run during PhASAR's compilation to indicate code smells if (PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD) @@ -275,44 +421,51 @@ if (PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD) ) endif () -# Add PhASAR's subdirectories -add_subdirectory(include) -add_subdirectory(lib) +# Library Dependency Dirs +if(NOT PHASAR_IN_TREE) + separate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS}) + add_definitions(${LLVM_DEFINITIONS_LIST}) +endif() -set(LLVM_LINK_COMPONENTS - coverage - coroutines - demangle - libdriver - lto - support - analysis - bitwriter - core - ipo - irreader - instcombine - instrumentation - linker - objcarcopts - scalaropts - transformutils - codegen - vectorize -) +# Installed config +configure_file(config.h.in include/phasar/Config/phasar-config.h @ONLY) -llvm_map_components_to_libnames(llvm_libs - ${LLVM_LINK_COMPONENTS} -) +# Warnings +option(PHASAR_ENABLE_WARNINGS "Enable warnings" ON) +if (PHASAR_ENABLE_WARNINGS) + if (MSVC) + string(APPEND CMAKE_CXX_FLAGS " /W4") + else() + string(APPEND CMAKE_CXX_FLAGS " -Wall -Wextra -Wno-unused-parameter") + endif() +endif (PHASAR_ENABLE_WARNINGS) + +# Some preprocessor symbols that need to be available in phasar sources, but should not be installed +add_cxx_compile_definitions(PHASAR_SRC_DIR="${CMAKE_SOURCE_DIR}") +add_cxx_compile_definitions(PHASAR_BUILD_DIR="${CMAKE_BINARY_DIR}") + +# Add PhASAR's subdirectories +add_subdirectory(lib) # phasar-based binaries add_subdirectory(tools) +# Swift tests +option(BUILD_SWIFT_TESTS "Builds the Swift tests (Swift compiler has to be installed manually beforehand!)" OFF) +if (BUILD_SWIFT_TESTS) + set(CMAKE_Swift_FLAGS_RELEASE "-g") + set(CMAKE_Swift_FLAGS_RELWITHDEBINFO "-g") + enable_language(Swift) +endif(BUILD_SWIFT_TESTS) + # Add Phasar unittests and build all IR test code if (PHASAR_BUILD_UNITTESTS) message("Phasar unittests") add_subdirectory(unittests) - set(PHASAR_BUILD_IR ON) + if(NOT PHASAR_BUILD_IR) + message(WARNING "Set PHASAR_BUILD_IR=ON, because PHASAR_BUILD_UNITTESTS is ON") + set(PHASAR_BUILD_IR ON) + endif() endif() # Build all IR test code @@ -321,7 +474,8 @@ if (PHASAR_BUILD_IR) add_subdirectory(test) endif() -set(INCLUDE_INSTALL_DIR include/ CACHE PATH "Install dir of headers") +set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_INCLUDEDIR}" CACHE PATH "Install dir of headers") +set(LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Install dir of libraries") # Install targets of phasar-cli, other executables, and libraries are to be # found in the individual subdirectories of tools/ @@ -334,17 +488,38 @@ install(DIRECTORY include/ PATTERN "*.h" ) -# Install the header only json container +# Install the config file +install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include/phasar/Config/ + DESTINATION include/phasar/Config + FILES_MATCHING + PATTERN "*.def" + PATTERN "*.h" +) + +if(NOT PHASAR_IN_TREE) + install(TARGETS phasar_interface + EXPORT PhasarExports + ) + + # Install the export-set containing all the phasar targets + install(EXPORT PhasarExports + FILE PhasarExports.cmake + NAMESPACE phasar:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar" + ) +else() + install(TARGETS phasar_interface + EXPORT LLVMExports + ) + set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS phasar_interface) +endif() + +# Install the header only json container ### TODO Fix this! install(DIRECTORY external/json/single_include/ DESTINATION include FILES_MATCHING PATTERN "*.hpp" ) -# Install the gtest header files (TODO this installation dependency should be eliminated) -install(DIRECTORY external/googletest/googletest/include/gtest/ - DESTINATION include/gtest -) - # Install Phasar utils helper scripts install(DIRECTORY utils/ DESTINATION bin @@ -370,21 +545,21 @@ configure_package_config_file( Config.cmake.in phasarConfig.cmake INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/phasar - PATH_VARS INCLUDE_INSTALL_DIR - ) + PATH_VARS INCLUDE_INSTALL_DIR LIBRARY_INSTALL_DIR +) write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/phasarConfigVersion.cmake - VERSION 1.0.0 + VERSION 2403 COMPATIBILITY SameMajorVersion - ) +) ### Install Config and ConfigVersion files install( FILES "${CMAKE_CURRENT_BINARY_DIR}/phasarConfig.cmake" "${CMAKE_CURRENT_BINARY_DIR}/phasarConfigVersion.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar" - ) +) # If the Phasar shared object libraries are not installed into a system folder # the so libs must be added manually to the linker search path and the linker @@ -406,62 +581,44 @@ set(MAJOR_VERSION 1) set(MINOR_VERSION 0) set(PATCH_VERSION 0) if (NOT PHASAR_IN_TREE) -IF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") -INCLUDE(InstallRequiredSystemLibraries) -set(CPACK_SET_DESTDIR "on") -set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") - -#set(CPACK_GENERATOR "DEB") -set(CPACK_GENERATOR "RPM") -set(CPACK_PACKAGE_DESCRIPTION "Phasar") -set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Phasar a LLVM-based static analysis framework") -set(CPACK_PACKAGE_VENDOR "Phasar Team - Philipp Schubert and others") -set(CPACK_PACKAGE_CONTACT "philipp.schubert@upb.de") -set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}") -set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") -set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") -set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}") -set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}") -# package dependencies can be set-up here -# better use autogenerated dependency information -set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) -# set(CPACK_DEBIAN_PACKAGE_DEPENDS "libboost_program_options (>= 1.66.0), -# libboost_graph (>= 1.66.0), -# libboost_thread (>= 1.66.0), -# libsqlite3 (>= 4.5.0), -# libpthread (>= 4.5.0), -# libdl (>= 4.5.0), -# librt (>= 4.5.0), -# libtinfo (>= 4.5.0), -# libz (>= 4.5.0), -# libm (>= 4.5.0), -# libstdc++ (>= 4.5.0), -# libgcc_s (>= 4.5.0), -# libc (>= 4.5.0), -# ld-linux-x86-64 (>= 4.5.0)") -set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") -set(CPACK_DEBIAN_PACKAGE_SECTION "kde") -set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) -set(CPACK_COMPONENTS_ALL Libraries ApplicationData) -INCLUDE(CPack) -ENDIF(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + if(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") + include(InstallRequiredSystemLibraries) + set(CPACK_SET_DESTDIR "on") + set(CPACK_PACKAGING_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") + + set(CPACK_GENERATOR "DEB") + set(CPACK_PACKAGE_DESCRIPTION "Phasar") + set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Phasar a LLVM-based static analysis framework") + set(CPACK_PACKAGE_VENDOR "Phasar Team - Philipp Schubert and others") + set(CPACK_PACKAGE_CONTACT "philipp.schubert@upb.de") + set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}") + set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") + set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") + set(CPACK_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}") + set(CPACK_SOURCE_PACKAGE_FILE_NAME "${CMAKE_PROJECT_NAME}_${MAJOR_VERSION}.${MINOR_VERSION}.${CPACK_PACKAGE_VERSION_PATCH}") + # package dependencies can be set-up here + # better use autogenerated dependency information + set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) + set(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") + set(CPACK_DEBIAN_PACKAGE_SECTION "kde") + set(CPACK_DEBIAN_ARCHITECTURE ${CMAKE_SYSTEM_PROCESSOR}) + set(CPACK_COMPONENTS_ALL Libraries ApplicationData) + include(CPack) + endif(EXISTS "${CMAKE_ROOT}/Modules/CPack.cmake") endif() # Setup the doxygen code documentation if(PHASAR_BUILD_DOC) - find_package(Doxygen) - if (DOXYGEN_FOUND) - set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in) - set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) - message("Doxygen build started") - add_custom_target(doc_doxygen ALL - COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMENT "Generating API documentation with Doxygen" - VERBATIM - ) - else(DOXYGEN_FOUND) - message(FATAL_ERROR "Doxygen need to be installed to generate the doxygen documentation.") - endif() + find_package(Doxygen REQUIRED) + + set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + add_custom_target(doc_doxygen ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating API documentation with Doxygen" + VERBATIM + ) endif() diff --git a/Config.cmake.in b/Config.cmake.in index 78e7bb723..305cf04e2 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -1,14 +1,23 @@ -set(PHASAR_VERSION 1.0.0) +set(PHASAR_VERSION 2403) @PACKAGE_INIT@ set_and_check(PHASAR_INCLUDE_DIR "@PACKAGE_INCLUDE_INSTALL_DIR@") +set_and_check(PHASAR_LIBRARY_DIR "@PACKAGE_LIBRARY_INSTALL_DIR@") include (CMakeFindDependencyMacro) find_dependency(nlohmann_json) find_dependency(nlohmann_json_schema_validator) -find_package(Boost 1.65.1 COMPONENTS program_options graph REQUIRED) -# TODO: The order seems to be important. Fix this! +find_package(Boost 1.65.1 COMPONENTS graph REQUIRED) +find_package(LLVM 14 REQUIRED CONFIG) + +set(PHASAR_USE_LLVM_FAT_LIB @USE_LLVM_FAT_LIB@) +set(PHASAR_BUILD_DYNLIB @PHASAR_BUILD_DYNLIB@) +set(PHASAR_USE_Z3 @PHASAR_USE_Z3@) + +if (PHASAR_USE_Z3) + find_dependency(Z3) +endif() set(PHASAR_COMPONENTS utils @@ -32,27 +41,37 @@ set(PHASAR_COMPONENTS controller ) -foreach(component ${PHASAR_COMPONENTS}) - include("${CMAKE_CURRENT_LIST_DIR}/phasar_${component}-targets.cmake") - list(APPEND PHASAR_NEEDED_LIBS phasar::phasar_${component}) +list(REMOVE_DUPLICATES phasar_FIND_COMPONENTS) + +set(PHASAR_NEEDED_LIBS) + +include("${CMAKE_CURRENT_LIST_DIR}/PhasarExports.cmake") + +foreach(component ${phasar_FIND_COMPONENTS}) + if(NOT ${component} IN_LIST PHASAR_COMPONENTS) + set(phasar_FOUND false) + set(phasar_NOT_FOUND_MESSAGE "Unsupported component: ${component}. Valid components are: ${PHASAR_COMPONENTS}") + endif() + + list(APPEND PHASAR_NEEDED_LIBS phasar::${component}) endforeach() -list(REMOVE_DUPLICATES PHASAR_NEEDED_LIBS) +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() -find_package(LLVM 14 REQUIRED CONFIG) -include_directories(${LLVM_INCLUDE_DIRS}) -link_directories(${LLVM_LIB_PATH} ${LLVM_LIBRARY_DIRS}) -find_library(LLVM_LIBRARY NAMES LLVM HINTS ${LLVM_LIBRARY_DIRS}) - -function(phasar_config executable) - if(NOT ${LLVM_LIBRARY} STREQUAL "LLVM_LIBRARY-NOTFOUND") - llvm_config(${executable} USE_SHARED ${PHASAR_LLVM_DEPS}) - else() - llvm_config(${executable} ${PHASAR_LLVM_DEPS}) + if (NOT phasar_FIND_COMPONENTS) + list(APPEND PHASAR_NEEDED_LIBS phasar::phasar) + # Default target + add_library(phasar ALIAS phasar::phasar) endif() - target_link_libraries(${executable} - PUBLIC - ${PHASAR_NEEDED_LIBS} - ) -endfunction() + function(phasar_config executable) + target_link_libraries(${executable} + PUBLIC + ${PHASAR_NEEDED_LIBS} + ) + endfunction() +endif() diff --git a/Dockerfile b/Dockerfile index f91375c66..2bed11717 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:22.04 ARG LLVM_INSTALL_DIR="/usr/local/llvm-14" -LABEL Name=phasar Version=1.0.0 +LABEL Name=phasar Version=2403 RUN apt -y update && apt install bash sudo -y @@ -15,7 +15,7 @@ RUN apt-get -y install --no-install-recommends \ cmake \ ninja-build \ libstdc++6 \ - libboost-all-dev + libboost-graph-dev COPY ./utils/InstallAptDependencies.sh /usr/src/phasar/utils/ RUN ./utils/InstallAptDependencies.sh @@ -53,6 +53,7 @@ RUN git submodule update RUN mkdir -p build && cd build && \ cmake .. \ -DCMAKE_BUILD_TYPE=Release \ + -DPHASAR_TARGET_ARCH="" \ -DCMAKE_CXX_COMPILER=$CXX \ -G Ninja && \ cmake --build . diff --git a/README.md b/README.md index 1c82c87c5..44ea59d4d 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,145 @@ ![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 0323 +Version 2403 -Secure Software Engineering Group ---------------------------------- +## Secure Software Engineering Group -+ Fabian Schiebel (fabian.schiebel@iem.fraunhofer.de), Martin Mory (martin.mory@upb.de), Philipp Dominik Schubert (philipp.schubert@upb.de) and others -+ Please also refer to https://phasar.org/ +PhASAR is primarily developed and maintained by the Secure Software Engineering Group at Heinz Nixdorf Institute (University of Paderborn) and Fraunhofer IEM. + +Lead developers of PhASAR are: Fabian Schiebel (@fabianbs96)(), Martin Mory (@MMory)(), Philipp Dominik Schubert (@pdschubert)() and others. + + + +## Required version of the C++ standard -Required version of the C++ standard ------------------------------------- PhASAR requires C++-17. -Currently supported version of LLVM ------------------------------------ -PhASAR is currently set up to support LLVM-14.0. +However, building in C++20 mode is supported as an experimental feature. You may enable this by turning the cmake option `PHASAR_EXPERIMENTAL_CXX20` on. +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. + +## Currently supported version of LLVM + +PhASAR is currently set up to support LLVM-14.0.* + +## What is PhASAR? -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 ----------------- +## 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 and then have a look on the material provided on https://phasar.org/ -as well. Please also have a look on PhASAR's project directory and notice the project directory -examples/ as well as the custom tool tools/myphasartool.cpp. +## 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 -Building PhASAR ---------------- -If you cannot work with one of the pre-built versions of PhASAR and would like to -compile PhASAR yourself, then please check the wiki for installing the -prerequisites and compilation. It is recommended to compile PhASAR yourself in -order to get the full C++ experience and to have full control over the build -mode. +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: +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 +./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 +./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). -Please help us to improve PhASAR --------------------------------- +## Please help us to improve PhASAR + You are using PhASAR and would like to help us in the future? Then please support us by filling out this [web form](https://goo.gl/forms/YG6m3M7sxeUJmKyi1). By giving us feedback you help to decide in what direction PhASAR should stride in the future and give us clues about our user base. Thank you very much! +## Installation -Installation ------------- PhASAR can be installed using the installer scripts as explained in the following. - ### 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 +./bootstrap.sh --install ``` The bootstrap script may ask for superuser permissions. Done! - ### Installing PhASAR 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 install 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. ### Compiling PhASAR (if not already done using the installation scripts) + 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++ ``` -$ 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: -``` -$ git submodule update --init +```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: -``` -$ utils/init-submodules-release.sh +```bash +utils/init-submodules-release.sh ``` Navigate into the PhASAR directory. The following commands will do the job and compile the PhASAR framework: -``` -$ 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 +```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/ directory: +After compilation using cmake the following two binaries can be found in the build/tools directory: -+ phasar-cli - the actual PhASAR command-line tool (previously called `phasar-llvm`) -+ myphasartool - an example tool that shows how tools can be build on top of PhASAR ++ `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 Use the command: @@ -146,47 +153,76 @@ When using CMake to compile PhASAR the following optional parameters can be used | Parameter : Type| Effect | |-----------|--------| -| BUILD_SHARED_LIBS : BOOL | Build shared libraries (default is ON) | -| CMAKE_BUILD_TYPE : STRING | Build PhASAR in 'Debug' 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_CONFIG_INSTALL_PREFIX : PATH | Path where PhASAR's configuration files will be installed if
ninja install” is invoked or the “install”
target is built. Expression will be evaluated within CMAKE_INSTALL_PREFIX, so prefer absolute paths (default is $(HOME)/.config/) | -| 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) | +| **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) | +| **PHASAR_EXPERIMENTAL_CXX20** : BOOL|Build phasar in C++20 mode. This is an experimental feature (default is OFF)| 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! +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/build_systems_tests/installation_tests/module.ll -D ifds-solvertest` +`$ phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest` If you obtain output other than a segmentation fault or an exception terminating the program abnormally everything works as expected. -How to use PhASAR? ------------------- -Please consult our [PhASAR wiki pages](https://github.com/secure-software-engineering/phasar/wiki). +## How to use PhASAR? + +We recomment using phasar as a library with `cmake`. + +If you already have installed phasar, [Use-PhASAR-as-a-library](https://github.com/secure-software-engineering/phasar/wiki/Using-Phasar-as-a-Library) may be a good start. + +Otherwise, we recommend adding PhASAR as a git submodule to your repository. +In this case, just `add_subdirectory` the phasar submodule directory and add phasar's include folder to your `include_directories` within your `CMakeLists.txt`. +Assuming you have checked out phasar in `external/phasar`, the phasar-related cmake commands may look like this: + +```cmake +set(PHASAR_BUILD_UNITTESTS OFF) # -- Don't build PhASAR's unittests with *your* tool +set(PHASAR_BUILD_IR OFF) # -- +add_subdirectory(external/phasar) # Build phasar with your tool +include_directories(external/phasar/include) # To find PhASAR's headers +link_libraries(nlohmann_json::nlohmann:json) # To find the json headers + +... + +target_link_libraries(yourphasartool + ... + phasar # Make your tool link against phasar +) +``` + +Depending on your use of PhASAR you also may need to add LLVM to your build. + +For more information please consult our [PhASAR wiki pages](https://github.com/secure-software-engineering/phasar/wiki). ### Installing PhASAR's Git pre-commit hook + You are very much welcome to contribute to the PhASAR project. Please make sure that you install our pre-commit hook that ensures your commit adheres to the most important coding rules of the PhASAR project. For more details please consult [Coding Conventions](https://github.com/secure-software-engineering/phasar/wiki/Coding-Conventions) and [Contributing to PhASAR](https://github.com/secure-software-engineering/phasar/wiki/Contributing-to-PhASAR). To install the pre-commit hook, please run the following commands in PhASAR's root directory: -``` -$ pip install pre-commit -$ pre-commit install - +```bash +pip install pre-commit +pre-commit install ``` Thanks. And have fun with the project. diff --git a/bootstrap.sh b/bootstrap.sh index c49d0a728..bb641b5da 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -5,16 +5,31 @@ set -eo pipefail source ./utils/safeCommandsSet.sh readonly PHASAR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" -readonly PHASAR_INSTALL_DIR="/usr/local/phasar" -readonly LLVM_INSTALL_DIR="/usr/local/llvm-14" +PHASAR_INSTALL_DIR="/usr/local/phasar" +LLVM_INSTALL_DIR="/usr/local/llvm-14" NUM_THREADS=$(nproc) -LLVM_RELEASE=llvmorg-14.0.0 +LLVM_RELEASE=llvmorg-14.0.6 DO_UNIT_TEST=true DO_INSTALL=false BUILD_TYPE=Release +function usage { + echo "USAGE: ./bootstrap.sh [options]" + echo "" + echo "OPTIONS:" + echo -e "\t--jobs\t\t-j\t\t- Number of parallel jobs used for compilation (default is nproc -- $(nproc))" + echo -e "\t--unittest\t-u\t\t- Build and run PhASARs unit-tests (default is true)" + echo -e "\t--install\t\t\t- Install PhASAR system-wide after building (default is false)" + echo -e "\t--help\t\t-h\t\t- Display this help message" + echo -e "\t-DBOOST_DIR=\t\t- The directory where boost should be installed (optional)" + echo -e "\t-DBOOST_VERSION=\t- The desired boost version to install (optional)" + echo -e "\t-DCMAKE_BUILD_TYPE=\t- The build mode for building PhASAR. One of {Debug, RelWithDebInfo, Release} (default is Release)" + echo -e "\t-DPHASAR_INSTALL_DIR=\t- The folder where to install PhASAR if --install is specified (default is ${PHASAR_INSTALL_DIR})" + echo -e "\t-DLLVM_INSTALL_DIR=\t- The folder where to install LLVM if --install is specified (default is ${LLVM_INSTALL_DIR})" +} + # Parsing command-line-parameters # See "https://stackoverflow.com/questions/192249/how-do-i-parse-command-line-arguments-in-bash" as a reference @@ -59,6 +74,28 @@ case $key in DO_INSTALL=true shift # past argument ;; + -DPHASAR_INSTALL_DIR) + PHASAR_INSTALL_DIR="$2" + shift # past argument + shift # past value + ;; + -DPHASAR_INSTALL_DIR=*) + PHASAR_INSTALL_DIR="${key#*=}" + shift # past argument=value + ;; + -DLLVM_INSTALL_DIR) + LLVM_INSTALL_DIR="$2" + shift # past argument + shift # past value + ;; + -DLLVM_INSTALL_DIR=*) + LLVM_INSTALL_DIR="${key#*=}" + shift # past argument=value + ;; + -h|--help) + usage + exit 0 + ;; *) # unknown option POSITIONAL+=("$1") # save it in an array for later shift # past argument @@ -71,30 +108,27 @@ set -- "${POSITIONAL[@]}" # restore positional parameters echo "installing phasar dependencies..." if [ -x "$(command -v pacman)" ]; then - yes | sudo pacman -Syu --needed which zlib sqlite3 ncurses make python3 doxygen libxml2 swig gcc z3 libedit graphviz python-sphinx openmp curl python-pip ninja - ./utils/installBuildEAR.sh + yes | sudo pacman -Syu --needed which zlib sqlite3 python3 doxygen gcc python-pip ninja cmake else ./utils/InstallAptDependencies.sh fi -pip3 install cmake - if [ ! -z "${DESIRED_BOOST_DIR}" ]; then BOOST_PARAMS="-DBOOST_ROOT=${DESIRED_BOOST_DIR}" else # New way of installing boost: # Check whether we have the required boost packages installed - BOOST_VERSION=$(echo -e '#include \nBOOST_LIB_VERSION' | gcc -s -x c++ -E - 2>/dev/null| grep "^[^#;]" | tr -d '\"') + { BOOST_VERSION=$(echo -e '#include \nBOOST_LIB_VERSION' | gcc -s -x c++ -E - 2>/dev/null| grep "^[^#;]" | tr -d '\"'); } || true if [ -z "$BOOST_VERSION" ] ;then if [ -x "$(command -v pacman)" ]; then yes | sudo pacman -Syu --needed boost-libs boost else if [ -z "$DESIRED_BOOST_VERSION" ] ;then - sudo apt install libboost-all-dev -y + sudo apt-get install libboost-graph-dev -y else # DESIRED_BOOST_VERSION in form d.d, i.e. 1.65 (this is the latest version I found in the apt repo) - sudo apt install "libboost${DESIRED_BOOST_VERSION}-all-dev" -y + sudo apt-get install "libboost${DESIRED_BOOST_VERSION}-graph-dev" -y fi #verify installation BOOST_VERSION=$(echo -e '#include \nBOOST_LIB_VERSION' | gcc -s -x c++ -E - 2>/dev/null| grep "^[^#;]" | tr -d '\"') @@ -110,9 +144,7 @@ else if [ -x "$(command -v apt)" ]; then DESIRED_BOOST_VERSION=${BOOST_VERSION//_/.} # install missing packages if necessary - boostlibnames=("libboost-system" "libboost-filesystem" - "libboost-graph" "libboost-program-options" - "libboost-thread") + boostlibnames=("libboost-graph") additional_boost_libs=() for boost_lib in ${boostlibnames[@]}; do dpkg -s "$boost_lib${DESIRED_BOOST_VERSION}" >/dev/null 2>&1 || @@ -123,7 +155,7 @@ else done if [ ${#additional_boost_libs[@]} -gt 0 ] ;then echo "Installing additional ${#additional_boost_libs[@]} boost packages: ${additional_boost_libs[*]}" - sudo apt install "${additional_boost_libs[@]}" -y || true + sudo apt-get install "${additional_boost_libs[@]}" -y || true fi fi fi @@ -135,6 +167,9 @@ tmp_dir=$(mktemp -d "llvm-build.XXXXXXXX" --tmpdir) rm -rf "${tmp_dir}" echo "dependencies successfully installed" +# *Always* set the LLVM root to ensure the Phasar script uses the proper toolchain +LLVM_PARAMS=-DLLVM_ROOT="${LLVM_INSTALL_DIR}" + echo "Updating the submodules..." git submodule update --init echo "Submodules successfully updated" @@ -147,7 +182,7 @@ export CXX=${LLVM_INSTALL_DIR}/bin/clang++ mkdir -p "${PHASAR_DIR}"/build safe_cd "${PHASAR_DIR}"/build -cmake -G Ninja -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" "${BOOST_PARAMS}" -DPHASAR_BUILD_UNITTESTS="${DO_UNIT_TEST}" "${PHASAR_DIR}" +cmake -G Ninja -DCMAKE_BUILD_TYPE="${BUILD_TYPE}" "${BOOST_PARAMS}" -DPHASAR_BUILD_UNITTESTS="${DO_UNIT_TEST}" "${LLVM_PARAMS}" "${PHASAR_DIR}" cmake --build . -j "${NUM_THREADS}" echo "phasar successfully built" diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 9fe7102f9..8499a4905 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -1,3 +1,18 @@ + +function(phasar_link_llvm executable) + # llvm_config links LLVM as PRIVATE. We need to link PUBLIC + if (USE_LLVM_FAT_LIB) + target_link_libraries(${executable} PUBLIC LLVM) + else() + llvm_map_components_to_libnames(LLVM_LIBRARIES ${ARGN}) + target_link_libraries(${executable} PUBLIC ${LLVM_LIBRARIES}) + endif() +endfunction() + +macro(add_cxx_compile_definitions) + add_compile_definitions("$<$:${ARGN}>") +endmacro() + function(add_phasar_unittest test_name) message("Set-up unittest: ${test_name}") get_filename_component(test ${test_name} NAME_WE) @@ -6,39 +21,12 @@ function(add_phasar_unittest test_name) ) add_dependencies(PhasarUnitTests ${test}) - if(USE_LLVM_FAT_LIB) - llvm_config(${test} USE_SHARED ${LLVM_LINK_COMPONENTS}) - else() - llvm_config(${test} ${LLVM_LINK_COMPONENTS}) - endif() + phasar_link_llvm(${test} ${LLVM_LINK_COMPONENTS}) target_link_libraries(${test} - LINK_PUBLIC - phasar_config - phasar_controller - phasar_llvm_controlflow - phasar_controlflow - phasar_llvm_utils - phasar_analysis_strategy - phasar_llvm_ifdside - phasar_utils - phasar_mono - phasar_llvm_db - phasar_db - # phasar_clang - phasar_passes - phasar_llvm_pointer - phasar_pointer - phasar_llvm_typehierarchy - phasar_llvm - phasar_taintconfig - nlohmann_json_schema_validator - ${SQLITE3_LIBRARY} - ${Boost_LIBRARIES} - ${CMAKE_DL_LIBS} - ${CMAKE_THREAD_LIBS_INIT} - curl - gtest + PRIVATE + phasar + gtest ) add_test(NAME "${test}" @@ -164,57 +152,64 @@ macro(add_phasar_executable name) ) endmacro(add_phasar_executable) -macro(add_phasar_library name) - set(srcs ${ARGN}) +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) + cmake_parse_arguments(PHASAR_LIB "${PHASAR_LIB_OPTIONS}" "" "${PHASAR_LIB_MULTIVAL}" ${ARGN}) + set(srcs ${PHASAR_LIB_UNPARSED_ARGUMENTS}) + list(APPEND srcs ${PHASAR_LIB_FILES}) if(MSVC_IDE OR XCODE) file(GLOB_RECURSE headers *.h *.td *.def) - set(srcs ${srcs} ${headers}) + list(APPEND srcs ${headers}) string(REGEX MATCHALL "/[^/]" split_path ${CMAKE_CURRENT_SOURCE_DIR}) list(GET split_path -1 dir) file(GLOB_RECURSE headers ../../include/phasar${dir}/*.h) - set(srcs ${srcs} ${headers}) + list(APPEND srcs ${headers}) endif(MSVC_IDE OR XCODE) - if(MODULE) + if(PHASAR_LIB_MODULE) set(libkind MODULE) - elseif(SHARED_LIBRARY) + elseif(PHASAR_LIB_INTERFACE) + set(libkind INTERFACE) + elseif(PHASAR_LIB_SHARED OR BUILD_SHARED_LIBS) set(libkind SHARED) else() - set(libkind) + set(libkind STATIC) endif() + # cut off prefix phasar_ for convenient component names + string(REGEX REPLACE phasar_ "" component_name ${name}) + add_library(${name} ${libkind} ${srcs}) - add_library(phasar::${name} ALIAS ${name}) + add_library(phasar::${component_name} ALIAS ${name}) + set_target_properties(${name} PROPERTIES + EXPORT_NAME ${component_name} + ) if(LLVM_COMMON_DEPENDS) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) - if(LLVM_USED_LIBS) - foreach(lib ${LLVM_USED_LIBS}) - target_link_libraries(${name} ${lib}) - endforeach(lib) - endif(LLVM_USED_LIBS) - - if(PHASAR_LINK_LIBS) - foreach(lib ${PHASAR_LINK_LIBS}) - if(PHASAR_DEBUG_LIBDEPS) - target_link_libraries(${name} LINK_PRIVATE ${lib}) - else() - target_link_libraries(${name} LINK_PUBLIC ${lib}) - endif(PHASAR_DEBUG_LIBDEPS) - endforeach(lib) - endif(PHASAR_LINK_LIBS) - - if(LLVM_LINK_COMPONENTS) - if(USE_LLVM_FAT_LIB) - llvm_config(${name} USE_SHARED ${LLVM_LINK_COMPONENTS}) - else() - llvm_config(${name} ${LLVM_LINK_COMPONENTS}) - endif() - endif(LLVM_LINK_COMPONENTS) + target_link_libraries(${name} PUBLIC ${PHASAR_LIB_LINKS} phasar_interface ${PHASAR_LIB_LINK_PUBLIC}) + if(PHASAR_DEBUG_LIBDEPS) + target_link_libraries(${name} PRIVATE -Wl,-z,defs) + endif() + + target_link_libraries(${name} PRIVATE ${PHASAR_LIB_LINK_PRIVATE}) + + phasar_link_llvm(${name} ${PHASAR_LIB_LLVM_LINK_COMPONENTS}) + + # Library Dependency Dirs + if(NOT PHASAR_IN_TREE) + target_include_directories(${name} PUBLIC + ${LLVM_INCLUDE_DIRS} + ) + target_link_directories(${name} PUBLIC + ${LLVM_LIB_PATH} ${LLVM_LIBRARY_DIRS} + ) + endif() if(MSVC) get_target_property(cflag ${name} COMPILE_FLAGS) @@ -227,60 +222,23 @@ macro(add_phasar_library name) set_target_properties(${name} PROPERTIES COMPILE_FLAGS ${cflag}) endif(MSVC) - # cut off prefix phasar_ for convenient component names - string(REGEX REPLACE phasar_ "" name component_name) - if(PHASAR_IN_TREE) install(TARGETS ${name} EXPORT LLVMExports LIBRARY DESTINATION lib - ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX}) + ARCHIVE DESTINATION lib${LLVM_LIBDIR_SUFFIX} + ) else() install(TARGETS ${name} - EXPORT ${name}-targets - COMPONENT ${component_name} - - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} - ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + EXPORT PhasarExports # NOTE: Library, archive and runtime destination are automatically set by # GNUInstallDirs which is included in the top-level CMakeLists.txt ) - install(EXPORT ${name}-targets - FILE ${name}-targets.cmake - NAMESPACE phasar:: - DESTINATION lib/cmake/phasar - COMPONENT ${component_name} - ) endif() set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS ${name}) -endmacro(add_phasar_library) - -macro(add_phasar_loadable_module name) - set(srcs ${ARGN}) - - # klduge: pass different values for MODULE with multiple targets in same dir - # this allows building shared-lib and module in same dir - # there must be a cleaner way to achieve this.... - if(MODULE) - else() - set(GLOBAL_NOT_MODULE TRUE) - endif() - - set(MODULE TRUE) - add_phasar_library(${name} ${srcs}) - - if(GLOBAL_NOT_MODULE) - unset(MODULE) - endif() - - if(APPLE) - # Darwin-specific linker flags for loadable modules. - set_target_properties(${name} PROPERTIES - LINK_FLAGS "-Wl,-flat_namespace -Wl,-undefined -Wl,suppress") - endif() -endmacro(add_phasar_loadable_module) +endfunction(add_phasar_library) macro(subdirlist result curdir) file(GLOB children RELATIVE ${curdir} ${curdir}/*) diff --git a/config.h.in b/config.h.in index ac0e9c09a..923329206 100644 --- a/config.h.in +++ b/config.h.in @@ -1,7 +1,12 @@ -#ifndef __CONFIG_H__ -#define __CONFIG_H__ +#ifndef PHASAR_CONFIG_CONFIG_H +#define PHASAR_CONFIG_CONFIG_H -#define PHASAR_SRC_DIR "@CMAKE_SOURCE_DIR@" -#define PHASAR_BUILD_DIR "@CMAKE_BINARY_DIR@" +#define PHASAR_CONFIG_DIR "@PHASAR_CONFIG_INSTALL_DIR@" -#endif /* __CONFIG_H__ */ +#cmakedefine PAMM_CORE +#cmakedefine PAMM_FULL + +#cmakedefine DYNAMIC_LOG +#cmakedefine BUILD_PHASAR_CLANG + +#endif /* PHASAR_CONFIG_CONFIG_H */ diff --git a/config/TaintConfigSchema.json b/config/TaintConfigSchema.json index d5599b517..25264e256 100644 --- a/config/TaintConfigSchema.json +++ b/config/TaintConfigSchema.json @@ -14,29 +14,36 @@ R"( "description": "Version of this taint configuration" }, "functions": { + "description": "Array of functions you want to include in your analysis", "type": "array", "properties": { "name": { - "type": "string" + "type": "string", + "description": "Name of the function in the llvm IR, look for possible mangling" }, "ret": { - "enum": ["source", "sink", "sanitizer"] + "enum": ["source", "sink", "sanitizer"], + "description": "Tags the returned value as source sink or sanitizer" }, "params": { + "description": "Tags function parameters as source, sink or sanitizer", "properties": { "source": { + "description": "Zero based indices of source tags", "type": "array", "properties": { "type": ["number", "string"] } }, "sink": { + "description": "Zero based indices of sink tags. A Leak is detected if the function gets called with source taged value in this indices.", "type": "array", "properties": { "type": ["number", "string"] } }, "sanitizer": { + "description": "Zero based indices of sanitizer tags", "type": "array", "properties": { "type": ["number", "string"] diff --git a/examples/use-phasar-as-library/CMakeLists.txt b/examples/use-phasar-as-library/CMakeLists.txt index 6a2ac73bf..a7bfaa8d9 100644 --- a/examples/use-phasar-as-library/CMakeLists.txt +++ b/examples/use-phasar-as-library/CMakeLists.txt @@ -7,18 +7,26 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) +find_package(phasar COMPONENTS llvm_ifdside REQUIRED) + +# Or without specifying components +# find_package(phasar REQUIRED) # Build a small test tool to show how phasar may be used add_executable(myphasartool myphasartool.cpp ) -find_package(phasar COMPONENTS ifdside REQUIRED) -include_directories(${PHASAR_INCLUDE_DIR}) -link_directories(${PHASAR_LIBRARY_DIR}) +# Old way using phasar_config: +# phasar_config(myphasartool) -phasar_config(myphasartool) +# New way using target_link_libraries: +target_link_libraries(myphasartool phasar::llvm_ifdside) +# If find_package did not specify components: +# target_link_libraries(myphasartool phasar::phasar) +# alternatively using the default target: +# target_link_libraries(myphasartool phasar) install(TARGETS myphasartool RUNTIME DESTINATION bin diff --git a/examples/use-phasar-as-library/README.md b/examples/use-phasar-as-library/README.md index 46aa31cb6..44dbce6ea 100644 --- a/examples/use-phasar-as-library/README.md +++ b/examples/use-phasar-as-library/README.md @@ -1 +1,6 @@ -This is a demo tool that uses PhASAR as a library. The number of phasar libraries explicitly stated in CMakeLists.txt can be further reduced by stating the non-transitive dependencies of phasar libraries. This is pending work on the PhASAR side. +# Use PhASAR as a Library + +This small example shows how you can setup a CMake project that uses PhASAR as a library. +This guide assumes that you have installed PhASAR such that the `find_package` cmake command can find it. + +You can choose the PhASAR components that you need in the `find_package` command. diff --git a/external/googletest b/external/googletest index e2239ee60..b796f7d44 160000 --- a/external/googletest +++ b/external/googletest @@ -1 +1 @@ -Subproject commit e2239ee6043f73722e7aa812a459f54a28552929 +Subproject commit b796f7d44681514f58a683a3a71ff17c94edb0c1 diff --git a/external/json b/external/json index 4f8fba140..bc889afb4 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit 4f8fba14066156b73f1189a2b8bd568bde5284c5 +Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d diff --git a/external/json-schema-validator b/external/json-schema-validator index 27fc1d094..491ac4402 160000 --- a/external/json-schema-validator +++ b/external/json-schema-validator @@ -1 +1 @@ -Subproject commit 27fc1d094503623dfe39365ba82581507524545c +Subproject commit 491ac44026e08f31790f5cacffa62e168bb35e32 diff --git a/githooks/pre-push b/githooks/pre-push index 2c0a6436f..6de881c12 100755 --- a/githooks/pre-push +++ b/githooks/pre-push @@ -5,7 +5,7 @@ local="$(git rev-parse --abbrev-ref HEAD)" if [ "$local" = "master" ] ; then -version="#define PHASAR_VERSION v" -version="${version}$(date +"%m%y")" -echo "${version}" > include/phasar/Config/Version.h + version="#define PHASAR_VERSION v" + version="${version}$(date +"%y%m")" + echo "${version}" > include/phasar/Config/Version.h fi diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt deleted file mode 100644 index 0e8996497..000000000 --- a/include/CMakeLists.txt +++ /dev/null @@ -1 +0,0 @@ -add_subdirectory(phasar) diff --git a/include/phasar.h b/include/phasar.h new file mode 100644 index 000000000..0444cf20f --- /dev/null +++ b/include/phasar.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2023 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_H +#define PHASAR_H + +#include "phasar/AnalysisStrategy.h" +#include "phasar/Config.h" +#include "phasar/ControlFlow.h" +#include "phasar/Controller.h" +#include "phasar/DB.h" +#include "phasar/DataFlow.h" +#include "phasar/Domain.h" +#include "phasar/PhasarLLVM.h" +#include "phasar/PhasarPass.h" +#include "phasar/Pointer.h" +#include "phasar/TypeHierarchy.h" +#include "phasar/Utils.h" + +#ifdef BUILD_PHASAR_CLANG +#include "phasar/PhasarClang.h" +#endif + +#endif // PHASAR_H diff --git a/include/phasar/AnalysisStrategy.h b/include/phasar/AnalysisStrategy.h new file mode 100644 index 000000000..6471b2e23 --- /dev/null +++ b/include/phasar/AnalysisStrategy.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2023 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_ANALYSISSTRATEGY_H +#define PHASAR_ANALYSISSTRATEGY_H + +#include "phasar/AnalysisStrategy/AnalysisSetup.h" +#include "phasar/AnalysisStrategy/DemandDrivenAnalysis.h" +#include "phasar/AnalysisStrategy/IncrementalUpdateAnalysis.h" +#include "phasar/AnalysisStrategy/ModuleWiseAnalysis.h" +#include "phasar/AnalysisStrategy/Strategies.h" +#include "phasar/AnalysisStrategy/VariationalAnalysis.h" + +#endif // PHASAR_ANALYSISSTRATEGY_H diff --git a/include/phasar/AnalysisStrategy/AnalysisSetup.h b/include/phasar/AnalysisStrategy/AnalysisSetup.h index 51edc8889..dcdfb6ee2 100644 --- a/include/phasar/AnalysisStrategy/AnalysisSetup.h +++ b/include/phasar/AnalysisStrategy/AnalysisSetup.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_ANALYSISSETUP_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_ANALYSISSETUP_H_ +#ifndef PHASAR_ANALYSISSTRATEGY_ANALYSISSETUP_H +#define PHASAR_ANALYSISSTRATEGY_ANALYSISSETUP_H #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" diff --git a/include/phasar/AnalysisStrategy/DemandDrivenAnalysis.h b/include/phasar/AnalysisStrategy/DemandDrivenAnalysis.h index 978a37244..1096f8f64 100644 --- a/include/phasar/AnalysisStrategy/DemandDrivenAnalysis.h +++ b/include/phasar/AnalysisStrategy/DemandDrivenAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_DEMANDDRIVENANALYSIS_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_DEMANDDRIVENANALYSIS_H_ +#ifndef PHASAR_ANALYSISSTRATEGY_DEMANDDRIVENANALYSIS_H +#define PHASAR_ANALYSISSTRATEGY_DEMANDDRIVENANALYSIS_H namespace psr { diff --git a/include/phasar/AnalysisStrategy/IncrementalUpdateAnalysis.h b/include/phasar/AnalysisStrategy/IncrementalUpdateAnalysis.h index b2e9e4b4d..e0aeb2cda 100644 --- a/include/phasar/AnalysisStrategy/IncrementalUpdateAnalysis.h +++ b/include/phasar/AnalysisStrategy/IncrementalUpdateAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_INCREMENTALUPDATEANALYSIS_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_INCREMENTALUPDATEANALYSIS_H_ +#ifndef PHASAR_ANALYSISSTRATEGY_INCREMENTALUPDATEANALYSIS_H +#define PHASAR_ANALYSISSTRATEGY_INCREMENTALUPDATEANALYSIS_H namespace psr { diff --git a/include/phasar/AnalysisStrategy/ModuleWiseAnalysis.h b/include/phasar/AnalysisStrategy/ModuleWiseAnalysis.h index 0fbf1fd86..7bc77acc4 100644 --- a/include/phasar/AnalysisStrategy/ModuleWiseAnalysis.h +++ b/include/phasar/AnalysisStrategy/ModuleWiseAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_MODULEWISEANALYSIS_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_MODULEWISEANALYSIS_H_ +#ifndef PHASAR_ANALYSISSTRATEGY_MODULEWISEANALYSIS_H +#define PHASAR_ANALYSISSTRATEGY_MODULEWISEANALYSIS_H namespace psr { diff --git a/include/phasar/AnalysisStrategy/Strategies.h b/include/phasar/AnalysisStrategy/Strategies.h index 16c17655e..acbf0fc9f 100644 --- a/include/phasar/AnalysisStrategy/Strategies.h +++ b/include/phasar/AnalysisStrategy/Strategies.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_STRATEGIES_H -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_STRATEGIES_H +#ifndef PHASAR_ANALYSISSTRATEGY_STRATEGIES_H +#define PHASAR_ANALYSISSTRATEGY_STRATEGIES_H #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" diff --git a/include/phasar/AnalysisStrategy/VariationalAnalysis.h b/include/phasar/AnalysisStrategy/VariationalAnalysis.h index 3211a0091..24b412213 100644 --- a/include/phasar/AnalysisStrategy/VariationalAnalysis.h +++ b/include/phasar/AnalysisStrategy/VariationalAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_VARIATIONALANALYSIS_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_VARIATIONALANALYSIS_H_ +#ifndef PHASAR_ANALYSISSTRATEGY_VARIATIONALANALYSIS_H +#define PHASAR_ANALYSISSTRATEGY_VARIATIONALANALYSIS_H #include "phasar/AnalysisStrategy/AnalysisSetup.h" diff --git a/include/phasar/CMakeLists.txt b/include/phasar/CMakeLists.txt deleted file mode 100644 index e69de29bb..000000000 diff --git a/include/phasar/Config.h b/include/phasar/Config.h new file mode 100644 index 000000000..81df120eb --- /dev/null +++ b/include/phasar/Config.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CONFIG_H +#define PHASAR_CONFIG_H + +#include "phasar/Config/Configuration.h" +#include "phasar/Config/Version.h" + +#endif // PHASAR_CONFIG_H diff --git a/include/phasar/Config/Configuration.h b/include/phasar/Config/Configuration.h index 24db00a2d..86528c338 100644 --- a/include/phasar/Config/Configuration.h +++ b/include/phasar/Config/Configuration.h @@ -28,9 +28,6 @@ #include #include -#define XSTR(S) STR(S) -#define STR(S) #S - namespace psr { class PhasarConfig { diff --git a/include/phasar/Config/Version.h b/include/phasar/Config/Version.h index ba61df9c8..4f595ee96 100644 --- a/include/phasar/Config/Version.h +++ b/include/phasar/Config/Version.h @@ -1,6 +1,6 @@ #ifndef PHASAR_CONFIG_VERSION_H #define PHASAR_CONFIG_VERSION_H -#define PHASAR_VERSION v0323 +#define PHASAR_VERSION v2403 #endif diff --git a/include/phasar/ControlFlow.h b/include/phasar/ControlFlow.h new file mode 100644 index 000000000..b820c0ec0 --- /dev/null +++ b/include/phasar/ControlFlow.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CONTROLFLOW_H +#define PHASAR_CONTROLFLOW_H + +#include "phasar/ControlFlow/CFGBase.h" +#include "phasar/ControlFlow/CallGraph.h" +#include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/ControlFlow/ICFGBase.h" +#include "phasar/ControlFlow/SpecialMemberFunctionType.h" + +#endif // PHASAR_CONTROLFLOW_H diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index bee58d4d8..57e358225 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_CFGBASE_H -#define PHASAR_PHASARLLVM_CONTROLFLOW_CFGBASE_H +#ifndef PHASAR_CONTROLFLOW_CFGBASE_H +#define PHASAR_CONTROLFLOW_CFGBASE_H #include "phasar/Utils/ByRef.h" #include "phasar/Utils/TypeTraits.h" @@ -150,4 +150,4 @@ constexpr bool is_cfg_v = is_crtp_base_of_v } // namespace psr -#endif // PHASAR_PHASARLLVM_CONTROLFLOW_CFGBASE_H +#endif // PHASAR_CONTROLFLOW_CFGBASE_H diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h new file mode 100644 index 000000000..590b16964 --- /dev/null +++ b/include/phasar/ControlFlow/CallGraph.h @@ -0,0 +1,304 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CONTROLFLOW_CALLGRAPH_H +#define PHASAR_CONTROLFLOW_CALLGRAPH_H + +#include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/StableVector.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" + +#include "nlohmann/json.hpp" + +#include +#include +#include + +namespace psr { +template class CallGraphBuilder; +template class CallGraph; + +template struct CGTraits> { + using n_t = N; + using f_t = F; +}; + +/// An explicit graph-representation of a call-graph. Only represents the data, +/// not the call-graph analysis that creates it. +/// +/// This type is immutable. To incrementally build it from your call-graph +/// analysis, use the CallGraphBuilder +template +class CallGraph : public CallGraphBase> { + using base_t = CallGraphBase>; + friend base_t; + friend class CallGraphBuilder; + +public: + using typename base_t::f_t; + using typename base_t::n_t; + using FunctionVertexTy = llvm::SmallVector; + using InstructionVertexTy = llvm::SmallVector; + + /// Creates a new, empty call-graph + CallGraph() noexcept = default; + + /// Deserializes a previously computed call-graph + template + [[nodiscard]] static CallGraph + deserialize(const nlohmann::json &PrecomputedCG, + FunctionGetter GetFunctionFromName, + InstructionGetter GetInstructionFromId); + + /// A range of all functions that are vertices in the call-graph. The number + /// of vertex functions can be retrieved by getNumVertexFunctions(). + [[nodiscard]] auto getAllVertexFunctions() const noexcept { + return llvm::make_first_range(CallersOf); + } + + /// A range of all call-sites that are vertices in the call-graph. The number + /// of vertex-callsites can be retrived by getNumVertexCallSites(). + [[nodiscard]] auto getAllVertexCallSites() const noexcept { + return llvm::make_first_range(CalleesAt); + } + + [[nodiscard]] size_t getNumVertexFunctions() const noexcept { + return CallersOf.size(); + } + [[nodiscard]] size_t getNumVertexCallSites() const noexcept { + return CalleesAt.size(); + } + + /// The number of functions within this call-graph + [[nodiscard]] size_t size() const noexcept { return getNumVertexFunctions(); } + + [[nodiscard]] bool empty() const noexcept { return CallersOf.empty(); } + + /// Creates a JSON representation of this call-graph suitable for presistent + /// storage. + /// Use the ctor taking a json object for deserialization + template + [[nodiscard]] 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, + InstParentGetter GetFunctionFromInst, + InstLabelGetter GetInstLabel) const { + OS << "digraph CallGraph{\n"; + scope_exit CloseBrace = [&OS] { OS << "}\n"; }; + + llvm::DenseMap Fun2Id; + Fun2Id.reserve(CallersOf.size()); + + size_t CurrId = 0; + for (const auto &Fun : getAllVertexFunctions()) { + OS << CurrId << "[label=\""; + OS.write_escaped(std::invoke(GetFunctionLabel, Fun)) << "\"];\n"; + Fun2Id[Fun] = CurrId++; + } + + for (const auto &[CS, Callees] : CalleesAt) { + const auto &Fun = std::invoke(GetFunctionFromInst, CS); + + for (const auto &Succ : *Callees) { + OS << Fun2Id.lookup(Fun) << "->" << Fun2Id.lookup(Succ) << "[label=\""; + OS.write_escaped(std::invoke(GetInstLabel, CS)) << "\"];\n"; + } + } + } + +private: + [[nodiscard]] llvm::ArrayRef + getCalleesOfCallAtImpl(ByConstRef Inst) const noexcept { + if (const auto *CalleesPtr = CalleesAt.lookup(Inst)) { + return *CalleesPtr; + } + return {}; + } + + [[nodiscard]] llvm::ArrayRef + getCallersOfImpl(ByConstRef Fun) const noexcept { + if (const auto *CallersPtr = CallersOf.lookup(Fun)) { + return *CallersPtr; + } + return {}; + } + + // --- + + StableVector InstVertexOwner; + std::vector FunVertexOwner; + + llvm::DenseMap CalleesAt{}; + llvm::DenseMap CallersOf{}; +}; + +/// A mutable wrapper over a CallGraph. Use this to build a call-graph from +/// within your call-graph ananlysis. +template class CallGraphBuilder { +public: + using n_t = typename CallGraph::n_t; + using f_t = typename CallGraph::f_t; + using FunctionVertexTy = typename CallGraph::FunctionVertexTy; + using InstructionVertexTy = typename CallGraph::InstructionVertexTy; + + void reserve(size_t MaxNumFunctions) { + CG.FunVertexOwner.reserve(MaxNumFunctions); + CG.CalleesAt.reserve(MaxNumFunctions); + CG.CallersOf.reserve(MaxNumFunctions); + } + + /// Registeres a new function in the call-graph. Returns a list of all + /// call-sites that are known so far to potentially call this function. + /// Do not manually add elements to this vector -- use addCallEdge instead. + [[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 && + "Trying to add more than MaxNumFunctions Function Vertices"); + It->second = &CG.FunVertexOwner.emplace_back(); + } + return It->second; + } + + /// Registeres a new call-site in the call-graph. Returns a list of all + /// callee functions that are known so far to potentially be called by this + /// function. + /// Do not manually add elements to this vector -- use addCallEdge instead. + [[nodiscard]] InstructionVertexTy *addInstructionVertex(n_t Inst) { + auto [It, Inserted] = CG.CalleesAt.try_emplace(std::move(Inst), nullptr); + if (Inserted) { + It->second = &CG.InstVertexOwner.emplace_back(); + } + return It->second; + } + + /// Tries to lookup the InstructionVertex for the given call-site. Returns + /// nullptr on failure. + [[nodiscard]] InstructionVertexTy * + getInstVertexOrNull(ByConstRef Inst) const noexcept { + return CG.CalleesAt.lookup(Inst); + } + + /// Adds a new directional edge to the call-graph indicating that CS may call + /// Callee + void addCallEdge(n_t CS, f_t Callee) { + auto IVtx = addInstructionVertex(CS); + auto FVtx = addFunctionVertex(Callee); + addCallEdge(std::move(CS), IVtx, std::move(Callee), FVtx); + } + + /// Same as addCallEdge(n_t, f_t), but uses an already known + /// InstructionVertexTy to save a lookup + void addCallEdge(n_t CS, InstructionVertexTy *Callees, f_t Callee) { + auto *Callers = addFunctionVertex(Callee); + addCallEdge(std::move(CS), Callees, std::move(Callee), Callers); + } + + /// Same as addCallEdge(n_t, f_t), but uses an already known + /// FunctionVertexTy to save a lookup + void addCallEdge(n_t CS, f_t Callee, FunctionVertexTy *Callers) { + auto *Callees = addInstructionVertex(CS); + addCallEdge(std::move(CS), Callees, std::move(Callee), Callers); + } + + /// Moves the completely built call-graph out of this builder for further + /// use. Do not use the builder after it anymore. + [[nodiscard]] CallGraph consumeCallGraph() noexcept { + return std::move(CG); + } + + /// Returns a view on the current (partial) call-graph that has already been + /// constructed + [[nodiscard]] const CallGraph &viewCallGraph() const noexcept { + return CG; + } + +private: + void addCallEdge(n_t CS, InstructionVertexTy *Callees, f_t Callee, + FunctionVertexTy *Callers) { + Callees->push_back(std::move(Callee)); + Callers->push_back(std::move(CS)); + } + + CallGraph CG{}; +}; + +template +template +[[nodiscard]] CallGraph +CallGraph::deserialize(const nlohmann::json &PrecomputedCG, + FunctionGetter GetFunctionFromName, + InstructionGetter GetInstructionFromId) { + if (!PrecomputedCG.is_object()) { + PHASAR_LOG_LEVEL_CAT(ERROR, "CallGraph", "Invalid Json. Expected object"); + return {}; + } + + CallGraphBuilder CGBuilder; + CGBuilder.reserve(PrecomputedCG.size()); + + for (const auto &[FunName, CallerIDs] : PrecomputedCG.items()) { + const auto &Fun = std::invoke(GetFunctionFromName, FunName); + if (!Fun) { + PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", + "Invalid function name: " << FunName); + continue; + } + + auto *CEdges = CGBuilder.addFunctionVertex(Fun); + CEdges->reserve(CallerIDs.size()); + + for (const auto &JId : CallerIDs) { + auto Id = JId.get(); + const auto &CS = std::invoke(GetInstructionFromId, Id); + if (!CS) { + PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", + "Invalid CAll-Instruction Id: " << Id); + } + + CGBuilder.addCallEdge(CS, Fun); + } + } + return CGBuilder.consumeCallGraph(); +} +} // namespace psr + +namespace llvm { +class Function; +class Instruction; +} // namespace llvm + +extern template class psr::CallGraph; +extern template class psr::CallGraphBuilder; + +#endif // PHASAR_CONTROLFLOW_CALLGRAPH_H diff --git a/include/phasar/ControlFlow/CallGraphAnalysisType.def b/include/phasar/ControlFlow/CallGraphAnalysisType.def index c136232c0..3430b21c0 100644 --- a/include/phasar/ControlFlow/CallGraphAnalysisType.def +++ b/include/phasar/ControlFlow/CallGraphAnalysisType.def @@ -16,6 +16,6 @@ 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") +CALL_GRAPH_ANALYSIS_TYPE(OTF, "otf", "On-the-fly analysis based on points-to info (default)") #undef CALL_GRAPH_ANALYSIS_TYPE diff --git a/include/phasar/ControlFlow/CallGraphAnalysisType.h b/include/phasar/ControlFlow/CallGraphAnalysisType.h index 31d6193ce..af5ed728b 100644 --- a/include/phasar/ControlFlow/CallGraphAnalysisType.h +++ b/include/phasar/ControlFlow/CallGraphAnalysisType.h @@ -7,8 +7,8 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_CALLGRAPHANALYSISTYPE_H -#define PHASAR_PHASARLLVM_CALLGRAPHANALYSISTYPE_H +#ifndef PHASAR_CONTROLFLOW_CALLGRAPHANALYSISTYPE_H +#define PHASAR_CONTROLFLOW_CALLGRAPHANALYSISTYPE_H #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -30,4 +30,4 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, CallGraphAnalysisType CGA); } // namespace psr -#endif // PHASAR_PHASARLLVM_CALLGRAPHANALYSISTYPE_H +#endif // PHASAR_CONTROLFLOW_CALLGRAPHANALYSISTYPE_H diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h new file mode 100644 index 000000000..2f2371618 --- /dev/null +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CONTROLFLOW_CALLGRAPHBASE_H +#define PHASAR_CONTROLFLOW_CALLGRAPHBASE_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/TypeTraits.h" + +namespace psr { +template struct CGTraits { + // using n_t + // using f_t +}; + +/// Base class of all CallGraph implementations within phasar (currently only +/// CallGraph). +/// Only represents the data, not how to create it. +template class CallGraphBase { +public: + using n_t = typename CGTraits::n_t; + using f_t = typename CGTraits::f_t; + + /// Returns an iterable range of all possible callee candidates at the given + /// call-site induced by the used call-graph. + /// + /// NOTE: This function is typically called in a hot part of the analysis and + /// should therefore be very fast + [[nodiscard]] decltype(auto) getCalleesOfCallAt(ByConstRef Inst) const + noexcept(noexcept(self().getCalleesOfCallAtImpl(Inst))) { + static_assert( + is_iterable_over_v); + return self().getCalleesOfCallAtImpl(Inst); + } + + /// Returns an iterable range of all possible call-site candidates that may + /// call the given function induced by the used call-graph. + [[nodiscard]] decltype(auto) getCallersOf(ByConstRef Fun) const { + static_assert( + is_iterable_over_v); + return self().getCallersOfImpl(Fun); + } + +private: + const Derived &self() const noexcept { + return static_cast(*this); + } +}; +} // namespace psr + +#endif // PHASAR_CONTROLFLOW_CALLGRAPHBASE_H diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index e85520de2..fea37796c 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -7,10 +7,11 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_ICFGBASE_H -#define PHASAR_PHASARLLVM_CONTROLFLOW_ICFGBASE_H +#ifndef PHASAR_CONTROLFLOW_ICFGBASE_H +#define PHASAR_CONTROLFLOW_ICFGBASE_H #include "phasar/ControlFlow/CFGBase.h" +#include "phasar/ControlFlow/CallGraphBase.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" @@ -64,20 +65,24 @@ template class ICFGBase { is_iterable_over_v); return self().allNonCallStartNodesImpl(); } + + /// Returns a view to the underlying call-graph + [[nodiscard]] decltype(auto) getCallGraph() const noexcept { + static_assert( + is_crtp_base_of_v>); + return self().getCallGraphImpl(); + } + /// Returns an iterable range of all possible callee candidates at the given - /// call-site induced by the used call-graph. NOTE: This function is typically - /// called in a hot part of the analysis and should therefore be very fast + /// call-site induced by the used call-graph. [[nodiscard]] decltype(auto) getCalleesOfCallAt(ByConstRef Inst) const { - static_assert( - is_iterable_over_v); - return self().getCalleesOfCallAtImpl(Inst); + return getCallGraph().getCalleesOfCallAt(Inst); } /// Returns an iterable range of all possible call-site candidates that may /// call the given function induced by the used call-graph. [[nodiscard]] decltype(auto) getCallersOf(ByConstRef Fun) const { - static_assert( - is_iterable_over_v); - return self().getCallersOfImpl(Fun); + return getCallGraph().getCallersOf(Fun); } /// Returns an iterable range of all call-instruction in the given function [[nodiscard]] decltype(auto) getCallsFromWithin(ByConstRef Fun) const { @@ -96,14 +101,7 @@ template class ICFGBase { n_t>); return self().getReturnSitesOfCallAtImpl(Inst); } - /// Returns an iterable range of all global initializer functions - [[nodiscard]] decltype(auto) - getGlobalInitializers(ByConstRef Fun) const { - static_assert( - is_iterable_over_v); - return self().getGlobalInitializersImpl(Fun); - } + /// Prints the underlying call-graph as DOT to the given output-stream void print(llvm::raw_ostream &OS = llvm::outs()) const { self().printImpl(OS); @@ -114,7 +112,6 @@ template class ICFGBase { } private: - Derived &self() noexcept { return static_cast(*this); } const Derived &self() const noexcept { return static_cast(*this); } @@ -130,4 +127,4 @@ constexpr bool is_icfg_v = is_crtp_base_of_v } // namespace psr -#endif // PHASAR_PHASARLLVM_CONTROLFLOW_ICFGBASE_H +#endif // PHASAR_CONTROLFLOW_ICFGBASE_H diff --git a/include/phasar/ControlFlow/SpecialMemberFunctionType.h b/include/phasar/ControlFlow/SpecialMemberFunctionType.h index b6e86f6c2..4b2957ccd 100644 --- a/include/phasar/ControlFlow/SpecialMemberFunctionType.h +++ b/include/phasar/ControlFlow/SpecialMemberFunctionType.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_SPECIALMEMBERFUNCTIONTYPE_H -#define PHASAR_PHASARLLVM_SPECIALMEMBERFUNCTIONTYPE_H +#ifndef PHASAR_CONTROLFLOW_SPECIALMEMBERFUNCTIONTYPE_H +#define PHASAR_CONTROLFLOW_SPECIALMEMBERFUNCTIONTYPE_H #include "llvm/ADT/StringRef.h" @@ -40,4 +40,4 @@ llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, SpecialMemberFunctionType SMFT); } // namespace psr -#endif // PHASAR_PHASARLLVM_SPECIALMEMBERFUNCTIONTYPE_H +#endif // PHASAR_CONTROLFLOW_SPECIALMEMBERFUNCTIONTYPE_H diff --git a/include/phasar/Controller.h b/include/phasar/Controller.h new file mode 100644 index 000000000..92d4c2202 --- /dev/null +++ b/include/phasar/Controller.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CONTROLLER_H +#define PHASAR_CONTROLLER_H + +#include "phasar/Controller/AnalysisController.h" +#include "phasar/Controller/AnalysisControllerEmitterOptions.h" + +#endif // PHASAR_CONTROLLER_H diff --git a/include/phasar/Controller/AnalysisController.h b/include/phasar/Controller/AnalysisController.h index 5ded47b64..7b9e132c4 100644 --- a/include/phasar/Controller/AnalysisController.h +++ b/include/phasar/Controller/AnalysisController.h @@ -13,159 +13,27 @@ #include "phasar/AnalysisStrategy/Strategies.h" #include "phasar/Controller/AnalysisControllerEmitterOptions.h" #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" -#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" -#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" -#include "phasar/DataFlow/IfdsIde/SolverResults.h" -#include "phasar/DataFlow/Mono/Solver/InterMonoSolver.h" -#include "phasar/DataFlow/Mono/Solver/IntraMonoSolver.h" -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/HelperAnalyses.h" -#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" -#include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" -#include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" -#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" -#include "phasar/Utils/EnumFlags.h" -#include "phasar/Utils/IO.h" -#include "phasar/Utils/Soundness.h" - -#include -#include -#include +#include namespace psr { class AnalysisController { -private: - HelperAnalyses &HA; - std::vector DataFlowAnalyses; - std::vector AnalysisConfigs; - std::vector EntryPoints; - [[maybe_unused]] AnalysisStrategy Strategy; - AnalysisControllerEmitterOptions EmitterOptions = - AnalysisControllerEmitterOptions::None; - std::string ProjectID; - std::filesystem::path ResultDirectory; - IFDSIDESolverConfig SolverConfig; - - /// - /// \brief The maximum length of the CallStrings used in the InterMonoSolver - /// - static const unsigned K = 3; - - void executeDemandDriven(); - - void executeIncremental(); - - void executeModuleWise(); - - void executeVariational(); - - void executeWholeProgram(); - - void emitRequestedHelperAnalysisResults(); - - void executeIFDSUninitVar(); - void executeIFDSConst(); - void executeIFDSTaint(); - void executeIFDSType(); - void executeIFDSSolverTest(); - void executeIFDSFieldSensTaint(); - void executeIDEXTaint(); - void executeIDEOpenSSLTS(); - void executeIDECSTDIOTS(); - void executeIDELinearConst(); - void executeIDESolverTest(); - void executeIDEIIA(); - void executeIntraMonoFullConstant(); - void executeIntraMonoSolverTest(); - void executeInterMonoSolverTest(); - void executeInterMonoTaint(); - - template - void executeMonoAnalysis(ArgTys &&...Args) { - auto Problem = - createAnalysisProblem(HA, std::forward(Args)...); - SolverTy Solver(Problem); - Solver.solve(); - emitRequestedDataFlowResults(Solver); - } - - template - void executeIntraMonoAnalysis(ArgTys &&...Args) { - executeMonoAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeInterMonoAnalysis(ArgTys &&...Args) { - executeMonoAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeIfdsIdeAnalysis(ArgTys &&...Args) { - auto Problem = - createAnalysisProblem(HA, std::forward(Args)...); - SolverTy Solver(Problem, &HA.getICFG()); - Solver.solve(); - emitRequestedDataFlowResults(Solver); - } - - template - void executeIFDSAnalysis(ArgTys &&...Args) { - executeIfdsIdeAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeIDEAnalysis(ArgTys &&...Args) { - executeIfdsIdeAnalysis, ProblemTy>( - std::forward(Args)...); - } - - LLVMTaintConfig makeTaintConfig(); - - template void emitRequestedDataFlowResults(T &Solver) { - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitTextReport) { - if (!ResultDirectory.empty()) { - if (auto OFS = - openFileStream(ResultDirectory.string() + "/psr-report.txt")) { - Solver.emitTextReport(*OFS); - } - } else { - Solver.emitTextReport(llvm::outs()); - } - } - if (EmitterOptions & - AnalysisControllerEmitterOptions::EmitGraphicalReport) { - if (!ResultDirectory.empty()) { - if (auto OFS = - openFileStream(ResultDirectory.string() + "/psr-report.html")) { - Solver.emitGraphicalReport(*OFS); - } - } else { - Solver.emitGraphicalReport(llvm::outs()); - } - } - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitRawResults) { - if (!ResultDirectory.empty()) { - if (auto OFS = openFileStream(ResultDirectory.string() + - "/psr-raw-results.txt")) { - Solver.dumpResults(*OFS); - } - } else { - Solver.dumpResults(llvm::outs()); - } - } - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitESGAsDot) { - llvm::outs() - << "Front-end support for 'EmitESGAsDot' to be implemented\n"; - } - } - public: + struct ControllerData { + HelperAnalyses *HA{}; + std::vector DataFlowAnalyses; + std::vector AnalysisConfigs; + std::vector EntryPoints; + [[maybe_unused]] AnalysisStrategy Strategy; + AnalysisControllerEmitterOptions EmitterOptions = + AnalysisControllerEmitterOptions::None; + std::string ProjectID; + std::filesystem::path ResultDirectory; + IFDSIDESolverConfig SolverConfig{}; + }; + explicit AnalysisController( HelperAnalyses &HA, std::vector DataFlowAnalyses, std::vector AnalysisConfigs, @@ -175,21 +43,15 @@ class AnalysisController { std::string ProjectID = "default-phasar-project", std::string OutDirectory = ""); - ~AnalysisController() = default; - - AnalysisController(const AnalysisController &) = delete; - AnalysisController(AnalysisController &&) = delete; - AnalysisController &operator=(const AnalysisController &) = delete; - AnalysisController &operator=(const AnalysisController &&) = delete; - - void executeAs(AnalysisStrategy Strategy); - static constexpr bool needsToEmitPTA(AnalysisControllerEmitterOptions EmitterOptions) { return (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsDot) || (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsJson) || (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsText); } + +private: + ControllerData Data; }; } // namespace psr diff --git a/include/phasar/DB.h b/include/phasar/DB.h new file mode 100644 index 000000000..f77b88f01 --- /dev/null +++ b/include/phasar/DB.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DB_H +#define PHASAR_DB_H + +#include "phasar/DB/Hexastore.h" +#include "phasar/DB/ProjectIRDBBase.h" +#include "phasar/DB/Queries.h" + +#endif // PHASAR_DB_H diff --git a/include/phasar/DB/ProjectIRDBBase.h b/include/phasar/DB/ProjectIRDBBase.h index 78c946417..092019686 100644 --- a/include/phasar/DB/ProjectIRDBBase.h +++ b/include/phasar/DB/ProjectIRDBBase.h @@ -112,7 +112,7 @@ template class ProjectIRDBBase { /// module for this function to work [[nodiscard]] size_t getInstructionId(n_t Inst) const { assert(isValid()); - return self().getInstructionId(Inst); + return self().getInstructionIdImpl(Inst); } [[nodiscard]] decltype(auto) getAllInstructions() const { diff --git a/include/phasar/DataFlow.h b/include/phasar/DataFlow.h new file mode 100644 index 000000000..88453a251 --- /dev/null +++ b/include/phasar/DataFlow.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DATAFLOW_H +#define PHASAR_DATAFLOW_H + +#include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.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/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h" +#include "phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/PathEdge.h" +#include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/DataFlow/IfdsIde/SpecialSummaries.h" +#include "phasar/DataFlow/Mono/Contexts/CallStringCTX.h" +#include "phasar/DataFlow/Mono/InterMonoProblem.h" +#include "phasar/DataFlow/Mono/IntraMonoProblem.h" +#include "phasar/DataFlow/Mono/Solver/InterMonoSolver.h" +#include "phasar/DataFlow/Mono/Solver/IntraMonoSolver.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManager.h" + +#endif // PHASAR_DATAFLOW_H diff --git a/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h b/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h index 4b631fe08..2d05cb54c 100644 --- a/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h +++ b/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H +#define PHASAR_DATAFLOW_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h" @@ -68,8 +68,9 @@ class DefaultEdgeFunctionSingletonCache if (LHS == RHS) { return true; } - auto Empty = llvm::DenseMapInfo::getEmptyKey(); - auto Tombstone = + const auto *Empty = + llvm::DenseMapInfo::getEmptyKey(); + const auto *Tombstone = llvm::DenseMapInfo::getTombstoneKey(); if (LHS == Empty || LHS == Tombstone || RHS == Empty || RHS == Tombstone) { @@ -114,4 +115,4 @@ class DefaultEdgeFunctionSingletonCache< } // namespace psr -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H +#endif // PHASAR_DATAFLOW_IFDSIDE_DEFAULTEDGEFUNCTIONSINGLETONCACHE_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index ca75fd53d..7f8e98df8 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -7,13 +7,15 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTION_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTION_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTION_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/TypeTraits.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/Casting.h" #include "llvm/Support/TypeName.h" @@ -21,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include @@ -58,13 +61,19 @@ template concept IsEdgeFunction = requires(const T &EF, const EdgeFunction& TEEF, EdgeFunctionRef CEF, typename T::l_t Src) { typename T::l_t; {EF.computeTarget(Src)} -> std::convertible_to; - {T::compose(CEF, TEEF)} -> std::convertible_to>; - {T::join(CEF, TEEF)} -> std::convertible_to>; + {T::compose(CEF, TEEF)} -> std::same_as>; + {T::join(CEF, TEEF)} -> std::same_as>; }; // clang-format on #endif +enum class EdgeFunctionAllocationPolicy { + SmallObjectOptimized, + DefaultHeapAllocated, + CustomHeapAllocated, +}; + class EdgeFunctionBase { public: template @@ -73,12 +82,9 @@ class EdgeFunctionBase { alignof(ConcreteEF) <= alignof(void *) && std::is_trivially_copyable_v; + using AllocationPolicy = EdgeFunctionAllocationPolicy; + protected: - enum class AllocationPolicy { - SmallObjectOptimized, - DefaultHeapAllocated, - CustomHeapAllocated, - }; struct RefCountedBase { mutable std::atomic_size_t Rc = 0; }; @@ -222,10 +228,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// Implicit-conversion constructor from EdgeFunctionRef. Increments the /// ref-count if not small-object optimized - template > && - IsEdgeFunction>> + template >>> EdgeFunction(EdgeFunctionRef CEF) noexcept : EdgeFunction(CEF.Instance, {&VTableFor, [CEF] { @@ -265,7 +269,7 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { : EdgeFunction( [](auto &&...Args) { if constexpr (IsSOOCandidate>) { - void *Ret; + void *Ret = nullptr; new (&Ret) ConcreteEF(std::forward(Args)...); return Ret; } else { @@ -302,7 +306,7 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { new (&Ret) ConcreteEF(std::move(EF.EF)); return Ret; } else { - if (auto Mem = EF.Cache->lookup(EF.EF)) { + if (const auto *Mem = EF.Cache->lookup(EF.EF)) { return static_cast *>(Mem); } @@ -328,7 +332,7 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// details. /// [[nodiscard]] l_t computeTarget(ByConstRef Source) const { - assert(!!*this && "computeTarget() called on nullptr!"); + assert(isValid() && "computeTarget() called on nullptr!"); return VTAndHeapAlloc.getPointer()->computeTarget(EF, Source); } @@ -359,8 +363,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// SecondEF.computeTarget(FirstEF.computeTarget(x)). [[nodiscard]] static EdgeFunction compose(const EdgeFunction &FirstEF, const EdgeFunction &SecondEF) { - assert(!!FirstEF && "compose() called on LHS nullptr!"); - assert(!!SecondEF && "compose() called on RHS nullptr!"); + assert(FirstEF.isValid() && "compose() called on LHS nullptr!"); + assert(SecondEF.isValid() && "compose() called on RHS nullptr!"); return FirstEF.VTAndHeapAlloc.getPointer()->compose( FirstEF.EF, SecondEF, FirstEF.VTAndHeapAlloc.getInt()); } @@ -400,12 +404,18 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// connected with the value-lattice on l_t [[nodiscard]] static EdgeFunction join(const EdgeFunction &FirstEF, const EdgeFunction &SecondEF) { - assert(!!FirstEF && "join() called on LHS nullptr!"); - assert(!!SecondEF && "join() called on RHS nullptr!"); + assert(FirstEF.isValid() && "join() called on LHS nullptr!"); + assert(SecondEF.isValid() && "join() called on RHS nullptr!"); return FirstEF.VTAndHeapAlloc.getPointer()->join( FirstEF.EF, SecondEF, FirstEF.VTAndHeapAlloc.getInt()); } + [[nodiscard]] bool + referenceEquals(const EdgeFunction &Other) const noexcept { + return VTAndHeapAlloc.getPointer() == Other.VTAndHeapAlloc.getPointer() && + EF == Other.EF; + } + /// Checks for equality of two edge functions. Equality requires exact /// type-equality and value-equality based on operator== of the concrete edge /// functions that are compared. @@ -548,15 +558,17 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// Allows for better optimizations in compose and join and should be /// provided, whehever this knowledge is available. [[nodiscard]] bool isConstant() const noexcept { - assert(!!*this && "isConstant() called on nullptr!"); + assert(isValid() && "isConstant() called on nullptr!"); return VTAndHeapAlloc.getPointer()->isConstant(EF); } - /// Performs a null-check. True, iff thie edge function is not null. - [[nodiscard]] explicit operator bool() const noexcept { + [[nodiscard]] bool isValid() const noexcept { return VTAndHeapAlloc.getOpaqueValue(); } + /// Performs a null-check. True, iff thie edge function is not null. + [[nodiscard]] explicit operator bool() const noexcept { return isValid(); } + /// Performs a runtime-typecheck. True, if the concrete type of the held edge /// function *exactly* equals ConcreteEF. /// @@ -611,6 +623,10 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return VTAndHeapAlloc.getInt() == AllocationPolicy::CustomHeapAllocated; } + [[nodiscard]] auto getAllocationPolicy() const noexcept { + return VTAndHeapAlloc.getInt(); + } + /// Gets an opaque identifier for this edge function. Only meant for /// comparisons of object-identity. Do not dereference! [[nodiscard]] const void *getOpaqueValue() const noexcept { return EF; } @@ -632,6 +648,36 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return static_cast *>(EF)->Cache; } + [[nodiscard]] size_t getHashCode() const noexcept { + if (!VTAndHeapAlloc.getOpaqueValue()) { + return 0; + } + + return VTAndHeapAlloc.getPointer()->getHashCode( + EF, VTAndHeapAlloc.getPointer()); + } + + [[nodiscard]] auto depth() const noexcept { + assert(isValid() && "depth() called on nullptr!"); + return VTAndHeapAlloc.getPointer()->depth(EF); + } + + friend size_t hash_value(const EdgeFunction &EF) noexcept { // NOLINT + return EF.getHashCode(); + } + + static EdgeFunction getEmptyKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getEmptyKey(), + AllocationPolicy::SmallObjectOptimized}); + } + + static EdgeFunction getTombstoneKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getTombstoneKey(), + AllocationPolicy::SmallObjectOptimized}); + } + private: struct VTable { // NOLINTBEGIN(readability-identifier-naming) @@ -643,6 +689,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { void (*print)(const void *, llvm::raw_ostream &); bool (*isConstant)(const void *) noexcept; void (*destroy)(const void *, AllocationPolicy) noexcept; + size_t (*getHashCode)(const void *, const void *) noexcept; + size_t (*depth)(const void *) noexcept; // NOLINTEND(readability-identifier-naming) }; @@ -706,6 +754,23 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { } } }, + [](const void *EF, const void *VT) noexcept -> size_t { + if constexpr (is_std_hashable_v) { + return std::hash{}(*getPtr(EF)); + } else if constexpr (is_llvm_hashable_v) { + using llvm::hash_value; + return hash_value(*getPtr(EF)); + } else { + return llvm::hash_combine(EF, VT); + } + }, + [](const void *EF) noexcept -> size_t { + if constexpr (HasDepth) { + return getPtr(EF)->depth(); + } else { + return 1; + } + }, }; // Utility ctor for (copy) construction. Increments the ref-count if @@ -731,6 +796,32 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { namespace llvm { +template struct DenseMapInfo> { + static inline auto getEmptyKey() noexcept { + return psr::EdgeFunction::getEmptyKey(); + } + static inline auto getTombstoneKey() noexcept { + return psr::EdgeFunction::getTombstoneKey(); + } + static inline auto getHashValue(const psr::EdgeFunction &EF) noexcept { + return EF.getHashCode(); + } + static inline auto isEqual(const psr::EdgeFunction &EF1, + const psr::EdgeFunction &EF2) noexcept { + if (EF1.referenceEquals(EF2)) { + return true; + } + auto Empty = getEmptyKey(); + auto Tombstone = getTombstoneKey(); + if (EF1.referenceEquals(Empty) || EF2.referenceEquals(Empty) || + EF1.referenceEquals(Tombstone) || EF2.referenceEquals(Tombstone)) { + return false; + } + + return EF1 == EF2; + } +}; + // LLVM is currently overhauling its casting system. Use the new variant once // possible! // Note: The new variant (With CastInfo) is not tested yet! @@ -806,4 +897,4 @@ struct CastInfo> #endif } // namespace llvm -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTION_H +#endif // PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTION_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h index d2aadfe6f..53e652c0f 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H #include "phasar/Utils/ByRef.h" @@ -72,4 +72,4 @@ CachedEdgeFunction(EdgeFunctionTy, EdgeFunctionSingletonCache *) } // namespace psr -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H +#endif // PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSINGLETONCACHE_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h new file mode 100644 index 000000000..448188b48 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +enum class EdgeFunctionKind; +enum class EdgeFunctionAllocationPolicy; + +namespace detail { +struct EdgeFunctionStatsData { + static constexpr size_t NumEFKinds = 5; + static constexpr size_t NumAllocPolicies = 3; + + std::array UniqueEFCount{}; + std::array TotalEFCount{}; + std::array PerAllocCount{}; + size_t MaxDepth{}; + double AvgDepth{}; + double AvgUniqueDepth{}; + + size_t TotalNumJF{}; + size_t UniqueNumJF{}; + size_t NumJFObjects{}; + size_t MaxJFDepth{}; + double AvgJFDepth{}; + double AvgUniqueJFDepth{}; + double AvgJFObjDepth{}; + std::array PerAllocJFCount{}; +}; +} // namespace detail + +class EdgeFunctionStats : public detail::EdgeFunctionStatsData { +public: + [[nodiscard]] size_t getNumUniqueEFs() const noexcept { + return std::reduce(UniqueEFCount.begin(), UniqueEFCount.end()); + } + [[nodiscard]] size_t getNumUniqueEFs(EdgeFunctionKind Kind) const noexcept { + assert(size_t(Kind) < NumEFKinds); + return UniqueEFCount[size_t(Kind)]; // NOLINT + } + + [[nodiscard]] size_t getNumEFs() const noexcept { + return std::reduce(TotalEFCount.begin(), TotalEFCount.end()); + } + [[nodiscard]] size_t getNumEFs(EdgeFunctionKind Kind) const noexcept { + assert(size_t(Kind) < NumEFKinds); + return TotalEFCount[size_t(Kind)]; // NOLINT + } + + [[nodiscard]] size_t getNumEFsPerAllocationPolicy( + EdgeFunctionAllocationPolicy Policy) const noexcept { + assert(size_t(Policy) < NumAllocPolicies); + return PerAllocCount[size_t(Policy)]; // NOLINT + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const EdgeFunctionStats &S); + +private: + template + friend class IDESolver; + + constexpr EdgeFunctionStats( + const detail::EdgeFunctionStatsData &Data) noexcept + : detail::EdgeFunctionStatsData(Data) {} +}; +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h index 7d07cc5fa..de9db609a 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h @@ -7,12 +7,13 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONUTILS_H_ -#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONUTILS_H_ +#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONUTILS_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONUTILS_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" @@ -265,12 +266,18 @@ template struct EdgeFunctionComposer { return LHS.First == RHS.First && LHS.Second == RHS.Second; } + [[nodiscard]] size_t depth() const noexcept { + return First.depth() + Second.depth(); + } + // -- data members EdgeFunction First{}; EdgeFunction Second{}; }; +static_assert(HasDepth>); + template struct JoinEdgeFunction { using l_t = L; using JLattice = JoinLatticeTraits; @@ -425,6 +432,12 @@ ConstantEdgeFunction::join(EdgeFunctionRef This, if (auto Default = defaultJoinOrNull(This, OtherFunction)) { return Default; } + + if (llvm::isa>(OtherFunction)) { + // Prevent endless recursion + return AllBottom{}; + } + if (!OtherFunction.isConstant()) { // do not know how to join; hence ask other function to decide on this return OtherFunction.joinWith(This); @@ -467,4 +480,4 @@ JoinEdgeFunction::join(EdgeFunctionRef This, } // namespace psr -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONUTILS_H +#endif // PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONUTILS_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h index 520cba7b1..3b9034133 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctions.h @@ -14,8 +14,8 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONS_H_ -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_EDGEFUNCTIONS_H_ +#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONS_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONS_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/Utils/ByRef.h" diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index 13fcb3bac..83520ba07 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -14,14 +14,15 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_FLOWFUNCTIONS_H_ -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_FLOWFUNCTIONS_H_ +#ifndef PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H +#define PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" #include +#include #include #include #include @@ -96,531 +97,527 @@ using FlowFunctionPtrTypeOf = std::shared_ptr; template > using FlowFunctionPtrType = FlowFunctionPtrTypeOf>; -/// A flow function that propagates all incoming facts unchanged. -/// -/// Given a flow-function f = identityFlow(), then for all incoming -/// dataflow-facts x, f(x) = {x}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// x1 x1 x3 ... -/// | | | ... -/// id-instruction | | | ... -/// v v v ... -/// x1 x2 x3 ... -/// -template > auto identityFlow() { - struct IdFF final : public FlowFunction { - Container computeTargets(D Source) override { return {std::move(Source)}; } - }; - static auto TheIdentity = std::make_shared(); - - return TheIdentity; -} +/// Wrapper flow function that is automatically used by the IDESolver if the +/// autoAddZero configuration option is set to true (default). +/// Ensures that the tautological zero-flow fact (Λ) does not get killed. +template > +class ZeroedFlowFunction : public FlowFunction { + using typename FlowFunction::container_type; + using typename FlowFunction::FlowFunctionPtrType; -/// The most generic flow function. Invokes the passed function object F to -/// retrieve the desired data-flows. -/// -/// So, given a flow function f = lambdaFlow(F), then for all incoming -/// dataflow-facts x, f(x) = F(x). -/// -/// In the exploded supergraph it may look as follows: -/// -/// x -/// | -/// inst F -/// / / | \ \ ... -/// v v v v v -/// x1 x2 x x3 x4 -/// -template , - typename = std::enable_if_t< - std::is_invocable_v && - std::is_convertible_v, Container>>> -auto lambdaFlow(Fn &&F) { - struct LambdaFlow final : public FlowFunction { - LambdaFlow(Fn &&F) : Flow(std::forward(F)) {} - Container computeTargets(D Source) override { - return std::invoke(Flow, std::move(Source)); +public: + ZeroedFlowFunction(FlowFunctionPtrType FF, D ZV) + : Delegate(std::move(FF)), ZeroValue(std::move(ZV)) {} + container_type computeTargets(D Source) override { + if (Source == ZeroValue) { + container_type Result = Delegate->computeTargets(Source); + Result.insert(ZeroValue); + return Result; } + return Delegate->computeTargets(std::move(Source)); + } - [[no_unique_address]] std::decay_t Flow; - }; +private: + FlowFunctionPtrType Delegate; + D ZeroValue; +}; - return std::make_shared(std::forward(F)); +namespace detail { +template +Container makeContainer(Range &&Rng) { + if constexpr (std::is_convertible_v, Container>) { + return std::forward(Rng); + } else { + Container C; + reserveIfPossible(C, Rng.size()); + for (auto &&Fact : Rng) { + C.insert(std::forward(Fact)); + } + return C; + } } +} // namespace detail //===----------------------------------------------------------------------===// -// Gen flow functions +// Flow Function Templates +//===----------------------------------------------------------------------===// -/// A flow function that generates a new dataflow fact (FactToGenerate) if -/// called with an already known dataflow fact (From). All other facts are -/// propagated like with the identityFlow. -/// -/// Given a flow function f = generateFlow(v, w), then for all incoming dataflow -/// facts x: -/// f(w) = {v, w}, -/// f(x) = {x}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// x w u ... -/// | |\ | ... -/// inst | | \ | ... -/// v v v v ... -/// x w v u -/// -/// \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 issue, use -/// transferFlow instead. -template > -auto generateFlow(psr::type_identity_t FactToGenerate, D From) { - struct GenFrom final : public FlowFunction { - GenFrom(D GenValue, D FromValue) - : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} - - Container computeTargets(D Source) override { - if (Source == FromValue) { - return {std::move(Source), GenValue}; - } - return {std::move(Source)}; - } +template class FlowFunctionTemplates { +public: + using d_t = D; + using container_type = Container; - D GenValue; - D FromValue; - }; + /// A flow function that propagates all incoming facts unchanged. + /// + /// Given a flow-function f = identityFlow(), then for all incoming + /// dataflow-facts x, f(x) = {x}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// x1 x1 x3 ... + /// | | | ... + /// id-instruction | | | ... + /// v v v ... + /// x1 x2 x3 ... + /// + static auto identityFlow() { + struct IdFF final : public FlowFunction { + container_type computeTargets(d_t Source) override { + return {std::move(Source)}; + } + }; + static auto TheIdentity = std::make_shared(); - return std::make_shared(std::move(FactToGenerate), std::move(From)); -} + return TheIdentity; + } -/// A flow function similar to generateFlow, that generates a new dataflow fact -/// (FactToGenerate), if the given Predicate evaluates to true on an incoming -/// dataflow fact -/// -/// So, given a flow function f = generateFlowIf(v, p), for all incoming -/// dataflow facts x: -/// f(x) = {v, x} if p(x) == true -/// f(x) = {x} else. -/// -template , - typename = std::enable_if_t>> -auto generateFlowIf(D FactToGenerate, Fn Predicate) { - struct GenFlowIf final : public FlowFunction { - GenFlowIf(D GenValue, Fn &&Predicate) - : GenValue(std::move(GenValue)), - Predicate(std::forward(Predicate)) {} - - Container computeTargets(D Source) override { - if (std::invoke(Predicate, Source)) { - return {std::move(Source), GenValue}; + /// The most generic flow function. Invokes the passed function object F to + /// retrieve the desired data-flows. + /// + /// So, given a flow function f = lambdaFlow(F), then for all incoming + /// dataflow-facts x, f(x) = F(x). + /// + /// In the exploded supergraph it may look as follows: + /// + /// x + /// | + /// inst F + /// / / | \ \ ... + /// v v v v v + /// x1 x2 x x3 x4 + /// + template static auto lambdaFlow(Fn &&F) { + struct LambdaFlow final : public FlowFunction { + LambdaFlow(Fn &&F) : Flow(std::forward(F)) {} + container_type computeTargets(d_t Source) override { + return std::invoke(Flow, std::move(Source)); } - return {std::move(Source)}; - } - D GenValue; - [[no_unique_address]] std::decay_t Predicate; - }; + [[no_unique_address]] std::decay_t Flow; + }; - return std::make_shared(std::move(FactToGenerate), - std::forward(Predicate)); -} + return std::make_shared(std::forward(F)); + } -/// A flow function that generates multiple new dataflow facts (FactsToGenerate) -/// if called from an already known dataflow fact (From). -/// -/// Given a flow function f = generateManyFlows({v1, v2, ..., vN}, w), for all -/// incoming dataflow facts x: -/// f(w) = {v1, v2, ..., vN, w} -/// f(x) = {x}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// x w u ... -/// | |\ \ ... \ | ... -/// inst | | \ \ ... \ | ... -/// v v v v ... \ v ... -/// x w v1 v2 ... vN u -/// -template , - typename = std::enable_if_t>> -auto generateManyFlows(Range &&FactsToGenerate, D From) { - struct GenMany final : public FlowFunction { - GenMany(Container &&GenValues, D FromValue) - : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} - - Container computeTargets(D Source) override { - if (Source == FromValue) { - auto Ret = GenValues; - Ret.insert(std::move(Source)); - return Ret; + //===----------------------------------------------------------------------===// + // Gen flow functions + + /// A flow function that generates a new dataflow fact (FactToGenerate) if + /// called with an already known dataflow fact (From). All other facts are + /// propagated like with the identityFlow. + /// + /// Given a flow function f = generateFlow(v, w), then for all incoming + /// dataflow facts x: + /// f(w) = {v, w}, + /// f(x) = {x}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// x w u ... + /// | |\ | ... + /// inst | | \ | ... + /// v v v v ... + /// x w v u + /// + /// \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 + /// issue, use transferFlow instead. + static auto generateFlow(d_t FactToGenerate, d_t From) { + struct GenFrom final : public FlowFunction { + GenFrom(d_t GenValue, d_t FromValue) + : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} + + container_type computeTargets(d_t Source) override { + if (Source == FromValue) { + return {std::move(Source), GenValue}; + } + return {std::move(Source)}; } - return {std::move(Source)}; - } - Container GenValues; - D FromValue; - }; - - auto MakeContainer = [](Range &&Rng) -> Container { - if constexpr (std::is_convertible_v, Container>) { - return std::forward(Rng); - } else { - Container C; - for (auto &&Fact : Rng) { - C.insert(std::forward(Fact)); - } - return C; - } - }; - return std::make_shared( - MakeContainer(std::forward(FactsToGenerate)), std::move(From)); -} + d_t GenValue; + d_t FromValue; + }; -//===----------------------------------------------------------------------===// -// Kill flow functions + return std::make_shared(std::move(FactToGenerate), + std::move(From)); + } -/// A flow function that stops propagating a specific dataflow fact -/// (FactToKill). -/// -/// Given a flow function f = killFlow(v), for all incoming dataflow facts x: -/// f(v) = {} -/// f(x) = {x} -/// -/// In the exploded supergraph it may look as follows: -/// -/// u v w ... -/// | | | -/// inst | | -/// v v -/// u v w ... -/// -template > -auto killFlow(D FactToKill) { - struct KillFlow final : public FlowFunction { - KillFlow(D KillValue) : KillValue(std::move(KillValue)) {} - Container computeTargets(D Source) override { - if (Source == KillValue) { - return {}; + /// A flow function similar to generateFlow, that generates a new dataflow + /// fact (FactToGenerate), if the given Predicate evaluates to true on an + /// incoming dataflow fact + /// + /// So, given a flow function f = generateFlowIf(v, p), for all incoming + /// dataflow facts x: + /// f(x) = {v, x} if p(x) == true + /// f(x) = {x} else. + /// + template >> + static auto generateFlowIf(d_t FactToGenerate, Fn Predicate) { + struct GenFlowIf final : public FlowFunction { + GenFlowIf(d_t GenValue, Fn &&Predicate) + : GenValue(std::move(GenValue)), + Predicate(std::forward(Predicate)) {} + + container_type computeTargets(d_t Source) override { + if (std::invoke(Predicate, Source)) { + return {std::move(Source), GenValue}; + } + return {std::move(Source)}; } - return {std::move(Source)}; - } - D KillValue; - }; - return std::make_shared(std::move(FactToKill)); -} + d_t GenValue; + [[no_unique_address]] std::decay_t Predicate; + }; -/// A flow function similar to killFlow that stops propagating all dataflow -/// facts for that the given Predicate evaluates to true. -/// -/// Given a flow function f = killFlowIf(p), for all incoming dataflow facts x: -/// f(x) = {} if p(x) == true -/// f(x) = {x} else. -/// -template , - typename = std::enable_if_t>> -auto killFlowIf(Fn Predicate) { - struct KillFlowIf final : public FlowFunction { - KillFlowIf(Fn &&Predicate) : Predicate(std::forward(Predicate)) {} - - Container computeTargets(D Source) override { - if (std::invoke(Predicate, Source)) { - return {}; + return std::make_shared(std::move(FactToGenerate), + std::forward(Predicate)); + } + + /// A flow function that generates multiple new dataflow facts + /// (FactsToGenerate) if called from an already known dataflow fact (From). + /// + /// Given a flow function f = generateManyFlows({v1, v2, ..., vN}, w), for all + /// incoming dataflow facts x: + /// f(w) = {v1, v2, ..., vN, w} + /// f(x) = {x}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// x w u ... + /// | |\ \ ... \ | ... + /// inst | | \ \ ... \ | ... + /// v v v v ... \ v ... + /// x w v1 v2 ... vN u + /// + template , + typename = std::enable_if_t>> + static auto generateManyFlows(Range &&FactsToGenerate, d_t From) { + struct GenMany final : public FlowFunction { + GenMany(container_type &&GenValues, d_t FromValue) + : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} + + container_type computeTargets(d_t Source) override { + if (Source == FromValue) { + auto Ret = GenValues; + Ret.insert(std::move(Source)); + return Ret; + } + return {std::move(Source)}; } - return {std::move(Source)}; - } - [[no_unique_address]] std::decay_t Predicate; - }; + container_type GenValues; + d_t FromValue; + }; - return std::make_shared(std::forward(Predicate)); -} + return std::make_shared(detail::makeContainer( + std::forward(FactsToGenerate)), + std::move(From)); + } -/// A flow function that stops propagating a specific set of dataflow facts -/// (FactsToKill). -/// -/// Given a flow function f = killManyFlows({v1, v2, ..., vN}), for all incoming -/// dataflow facts x: -/// f(v1) = {} -/// f(v2) = {} -/// ... -/// f(vN) = {} -/// f(x) = {x}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// u v1 v2 ... vN w ... -/// | | | | | -/// inst | | -/// v v -/// u v1 v2 ... vN w ... -/// -template , - typename = std::enable_if_t>> -auto killManyFlows(Range &&FactsToKill) { - struct KillMany final : public FlowFunction { - KillMany(Container &&KillValues) : KillValues(std::move(KillValues)) {} - - Container computeTargets(D Source) override { - if (KillValues.count(Source)) { - return {}; + //===----------------------------------------------------------------------===// + // Kill flow functions + + /// A flow function that stops propagating a specific dataflow fact + /// (FactToKill). + /// + /// Given a flow function f = killFlow(v), for all incoming dataflow facts x: + /// f(v) = {} + /// f(x) = {x} + /// + /// In the exploded supergraph it may look as follows: + /// + /// u v w ... + /// | | | + /// inst | | + /// v v + /// u v w ... + /// + static auto killFlow(d_t FactToKill) { + struct KillFlow final : public FlowFunction { + KillFlow(d_t KillValue) : KillValue(std::move(KillValue)) {} + container_type computeTargets(d_t Source) override { + if (Source == KillValue) { + return {}; + } + return {std::move(Source)}; } - return {std::move(Source)}; - } + d_t KillValue; + }; - Container KillValues; - }; + return std::make_shared(std::move(FactToKill)); + } - auto MakeContainer = [](Range &&Rng) -> Container { - if constexpr (std::is_convertible_v, Container>) { - return std::forward(Rng); - } else { - Container C; - for (auto &&Fact : Rng) { - C.insert(std::forward(Fact)); + /// A flow function similar to killFlow that stops propagating all dataflow + /// facts for that the given Predicate evaluates to true. + /// + /// Given a flow function f = killFlowIf(p), for all incoming dataflow facts + /// x: + /// f(x) = {} if p(x) == true + /// f(x) = {x} else. + /// + template >> + static auto killFlowIf(Fn Predicate) { + struct KillFlowIf final : public FlowFunction { + KillFlowIf(Fn &&Predicate) : Predicate(std::forward(Predicate)) {} + + container_type computeTargets(d_t Source) override { + if (std::invoke(Predicate, Source)) { + return {}; + } + return {std::move(Source)}; } - return C; - } - }; - return std::make_shared( - MakeContainer(std::forward(FactsToKill))); -} - -/// A flow function that stops propagating *all* incoming dataflow facts. -/// -/// Given a flow function f = killAllFlows(), for all incoming dataflow facts x, -/// f(x) = {}. -/// -template > auto killAllFlows() { - struct KillAllFF final : public FlowFunction { - Container computeTargets(D Source) override { return {std::move(Source)}; } - }; - static auto TheKillAllFlow = std::make_shared(); - return TheKillAllFlow; -} + [[no_unique_address]] std::decay_t Predicate; + }; -//===----------------------------------------------------------------------===// -// Gen-and-kill flow functions + return std::make_shared(std::forward(Predicate)); + } -/// A flow function that composes kill and generate flow functions. -/// Like generateFlow it generates a new dataflow fact (FactToGenerate), if -/// called with a specific dataflow fact (From). -/// However, like killFlowIf it stops propagating all other dataflow facts. -/// -/// Given a flow function f = generateFlowAndKillAllOthers(v, w), for all -/// incoming dataflow facts x: -/// f(w) = {v, w} -/// f(x) = {}. -/// -/// Equivalent to: killFlowIf(λz.z!=w) o generateFlow(v, w) (where o denotes -/// function composition) -/// -/// In the exploded supergraph it may look as follows: -/// -/// x w u ... -/// | |\ | -/// inst | \ ... -/// v v -/// x w v u -/// -template > -auto generateFlowAndKillAllOthers(psr::type_identity_t FactToGenerate, - D From) { - struct GenFlowAndKillAllOthers final : public FlowFunction { - GenFlowAndKillAllOthers(D GenValue, D FromValue) - : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} - - Container computeTargets(D Source) override { - if (Source == FromValue) { - return {std::move(Source), GenValue}; + /// A flow function that stops propagating a specific set of dataflow facts + /// (FactsToKill). + /// + /// Given a flow function f = killManyFlows({v1, v2, ..., vN}), for all + /// incoming dataflow facts x: + /// f(v1) = {} + /// f(v2) = {} + /// ... + /// f(vN) = {} + /// f(x) = {x}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// u v1 v2 ... vN w ... + /// | | | | | + /// inst | | + /// v v + /// u v1 v2 ... vN w ... + /// + template , + typename = std::enable_if_t>> + static auto killManyFlows(Range &&FactsToKill) { + struct KillMany final : public FlowFunction { + KillMany(Container &&KillValues) : KillValues(std::move(KillValues)) {} + + container_type computeTargets(d_t Source) override { + if (KillValues.count(Source)) { + return {}; + } + return {std::move(Source)}; } - return {}; - } - D GenValue; - D FromValue; - }; + container_type KillValues; + }; - return std::make_shared(std::move(FactToGenerate), - std::move(From)); -} + return std::make_shared(detail::makeContainer( + std::forward(FactsToKill))); + } -/// A flow function similar to generateFlowAndKillAllOthers that may generate -/// multiple dataflow facts (FactsToGenerate) is called with a specific fact -/// (From) and stops propagating all other dataflow facts. -/// -/// Given a flow function f = generateManyFlowsAndKillAllOthers({v1, v2, ..., -/// vN}, w), for all incoming dataflow facts x: -/// f(w) = {v1, v2, ..., vN, w} -/// f(x) = {}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// x w u ... -/// | |\ \ ... \ | ... -/// inst | \ \ ... \ ... -/// v v v ... \ ... -/// x w v1 v2 ... vN u -/// -template , - typename = std::enable_if_t>> -auto generateManyFlowsAndKillAllOthers(Range &&FactsToGenerate, D From) { - struct GenManyAndKillAllOthers final : public FlowFunction { - GenManyAndKillAllOthers(Container &&GenValues, D FromValue) - : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} - - Container computeTargets(D Source) override { - if (Source == FromValue) { - auto Ret = GenValues; - Ret.insert(std::move(Source)); - return Ret; - } - return {}; - } + /// A flow function that stops propagating *all* incoming dataflow facts. + /// + /// Given a flow function f = killAllFlows(), for all incoming dataflow facts + /// x, f(x) = {}. + /// + static auto killAllFlows() { + struct KillAllFF final : public FlowFunction { + Container computeTargets(d_t /*Source*/) override { return Container(); } + }; + static auto TheKillAllFlow = std::make_shared(); + + return TheKillAllFlow; + } - Container GenValues; - D FromValue; - }; - - auto MakeContainer = [](Range &&Rng) -> Container { - if constexpr (std::is_convertible_v, Container>) { - return std::forward(Rng); - } else { - Container C; - for (auto &&Fact : Rng) { - C.insert(std::forward(Fact)); + //===----------------------------------------------------------------------===// + // Gen-and-kill flow functions + + /// A flow function that composes kill and generate flow functions. + /// Like generateFlow it generates a new dataflow fact (FactToGenerate), if + /// called with a specific dataflow fact (From). + /// However, like killFlowIf it stops propagating all other dataflow facts. + /// + /// Given a flow function f = generateFlowAndKillAllOthers(v, w), for all + /// incoming dataflow facts x: + /// f(w) = {v, w} + /// f(x) = {}. + /// + /// Equivalent to: killFlowIf(λz.z!=w) o generateFlow(v, w) (where o denotes + /// function composition) + /// + /// In the exploded supergraph it may look as follows: + /// + /// x w u ... + /// | |\ | + /// inst | \ ... + /// v v + /// x w v u + /// + static auto generateFlowAndKillAllOthers(d_t FactToGenerate, d_t From) { + struct GenFlowAndKillAllOthers final + : public FlowFunction { + GenFlowAndKillAllOthers(d_t GenValue, d_t FromValue) + : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} + + container_type computeTargets(d_t Source) override { + if (Source == FromValue) { + return {std::move(Source), GenValue}; + } + return {}; } - return C; - } - }; - return std::make_shared( - MakeContainer(std::forward(FactsToGenerate)), std::move(From)); -} -//===----------------------------------------------------------------------===// -// Miscellaneous flow functions + d_t GenValue; + d_t FromValue; + }; -/// A flow function that, similar to generateFlow, generates a new dataflow fact -/// (FactsToGenerate) when called with a specific dataflow fact (From). -/// Unlike generateFlow, it kills FactToGenerate if it is part of the incoming -/// facts. THis has no additional effect for IFDS analyses (which in fact should -/// use generateFlow instead), but for IDE analyses it may avoid joining the -/// edge functions reaching the FactToGenerate together which may improve the -/// analysis' precision. -/// -/// Given a flow function f = transferFlow(v, w), for all incoming dataflow -/// facts x: -/// f(v) = {} -/// f(w) = {v, w} -/// f(x) = {x}. -/// -/// In the exploded supergraph it may look as follows: -/// -/// x w v u ... -/// | |\ | | ... -/// | | \ | ... -/// inst | | \ | ... -/// v v v v ... -/// x w v u -/// -template > -auto transferFlow(psr::type_identity_t FactToGenerate, D From) { - struct TransferFlow final : public FlowFunction { - TransferFlow(D GenValue, D FromValue) - : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} - - Container computeTargets(D Source) override { - if (Source == FromValue) { - return {std::move(Source), GenValue}; - } - if (Source == GenValue) { + return std::make_shared(std::move(FactToGenerate), + std::move(From)); + } + + /// A flow function similar to generateFlowAndKillAllOthers that may generate + /// multiple dataflow facts (FactsToGenerate) is called with a specific fact + /// (From) and stops propagating all other dataflow facts. + /// + /// Given a flow function f = generateManyFlowsAndKillAllOthers({v1, v2, ..., + /// vN}, w), for all incoming dataflow facts x: + /// f(w) = {v1, v2, ..., vN, w} + /// f(x) = {}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// x w u ... + /// | |\ \ ... \ | ... + /// inst | \ \ ... \ ... + /// v v v ... \ ... + /// x w v1 v2 ... vN u + /// + template , + typename = std::enable_if_t>> + static auto generateManyFlowsAndKillAllOthers(Range &&FactsToGenerate, + d_t From) { + struct GenManyAndKillAllOthers final + : public FlowFunction { + GenManyAndKillAllOthers(Container &&GenValues, d_t FromValue) + : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} + + container_type computeTargets(d_t Source) override { + if (Source == FromValue) { + auto Ret = GenValues; + Ret.insert(std::move(Source)); + return Ret; + } return {}; } - return {std::move(Source)}; - } - D GenValue; - D FromValue; - }; + container_type GenValues; + d_t FromValue; + }; - return std::make_shared(std::move(FactToGenerate), - std::move(From)); -} + return std::make_shared( + detail::makeContainer( + std::forward(FactsToGenerate)), + std::move(From)); + } -/// A flow function that takes two other flow functions OneFF and OtherFF and -/// applies both flow functions on each input merging the results together with -/// set-union. -/// -/// Given a flow function f = unionFlows(g, h), for all incoming dataflow facts -/// x: -/// f(x) = g(x) u h(x). (where u denotes set-union) -/// -template && - std::is_same_v>> -auto unionFlows(FlowFunctionPtrTypeOf OneFF, - FlowFunctionPtrTypeOf OtherFF) { - struct UnionFlow final : public FlowFunction { - UnionFlow(FlowFunctionPtrTypeOf OneFF, - FlowFunctionPtrTypeOf OtherFF) noexcept - : OneFF(std::move(OneFF)), OtherFF(std::move(OtherFF)) {} - - Container computeTargets(D Source) override { - auto OneRet = OneFF->computeTargets(Source); - auto OtherRet = OtherFF->computeTargets(std::move(Source)); - if (OneRet.size() < OtherRet.size()) { - std::swap(OneRet, OtherRet); + //===----------------------------------------------------------------------===// + // Miscellaneous flow functions + + /// A flow function that, similar to generateFlow, generates a new dataflow + /// fact (FactsToGenerate) when called with a specific dataflow fact (From). + /// Unlike generateFlow, it kills FactToGenerate if it is part of the incoming + /// facts. THis has no additional effect for IFDS analyses (which in fact + /// should use generateFlow instead), but for IDE analyses it may avoid + /// joining the edge functions reaching the FactToGenerate together which may + /// improve the analysis' precision. + /// + /// Given a flow function f = transferFlow(v, w), for all incoming dataflow + /// facts x: + /// f(v) = {} + /// f(w) = {v, w} + /// f(x) = {x}. + /// + /// In the exploded supergraph it may look as follows: + /// + /// x w v u ... + /// | |\ | | ... + /// | | \ | ... + /// inst | | \ | ... + /// v v v v ... + /// x w v u + /// + static auto transferFlow(d_t FactToGenerate, d_t From) { + struct TransferFlow final : public FlowFunction { + TransferFlow(d_t GenValue, d_t FromValue) + : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} + + container_type computeTargets(d_t Source) override { + if (Source == FromValue) { + return {std::move(Source), GenValue}; + } + if (Source == GenValue) { + return {}; + } + return {std::move(Source)}; } - OneRet.insert(std::make_move_iterator(OtherRet.begin()), - std::make_move_iterator(OtherRet.end())); - return OneRet; - } + d_t GenValue; + d_t FromValue; + }; - FlowFunctionPtrTypeOf OneFF; - FlowFunctionPtrTypeOf OtherFF; - }; + return std::make_shared(std::move(FactToGenerate), + std::move(From)); + } - return std::make_shared(std::move(OneFF), std::move(OtherFF)); -} + /// A flow function that takes two other flow functions OneFF and OtherFF and + /// applies both flow functions on each input merging the results together + /// with set-union. + /// + /// Given a flow function f = unionFlows(g, h), for all incoming dataflow + /// facts x: + /// f(x) = g(x) u h(x). (where u denotes set-union) + /// + template && + std::is_same_v && + std::is_same_v>> + auto unionFlows(FlowFunctionPtrTypeOf OneFF, + FlowFunctionPtrTypeOf OtherFF) { + struct UnionFlow final : public FlowFunction { + UnionFlow(FlowFunctionPtrTypeOf OneFF, + FlowFunctionPtrTypeOf OtherFF) noexcept + : OneFF(std::move(OneFF)), OtherFF(std::move(OtherFF)) {} + + container_type computeTargets(d_t Source) override { + auto OneRet = OneFF->computeTargets(Source); + auto OtherRet = OtherFF->computeTargets(std::move(Source)); + if (OneRet.size() < OtherRet.size()) { + std::swap(OneRet, OtherRet); + } + + OneRet.insert(std::make_move_iterator(OtherRet.begin()), + std::make_move_iterator(OtherRet.end())); + return OneRet; + } -/// Wrapper flow function that is automatically used by the IDESolver if the -/// autoAddZero configuration option is set to true (default). -/// Ensures that the tautological zero-flow fact (Λ) does not get killed. -template > -class ZeroedFlowFunction : public FlowFunction { - using typename FlowFunction::container_type; - using typename FlowFunction::FlowFunctionPtrType; + FlowFunctionPtrTypeOf OneFF; + FlowFunctionPtrTypeOf OtherFF; + }; -public: - ZeroedFlowFunction(FlowFunctionPtrType FF, D ZV) - : Delegate(std::move(FF)), ZeroValue(ZV) {} - container_type computeTargets(D Source) override { - if (Source == ZeroValue) { - container_type Result = Delegate->computeTargets(Source); - Result.insert(ZeroValue); - return Result; - } - return Delegate->computeTargets(Source); + return std::make_shared(std::move(OneFF), std::move(OtherFF)); } - -private: - FlowFunctionPtrType Delegate; - D ZeroValue; }; //===----------------------------------------------------------------------===// // FlowFunctions Class //===----------------------------------------------------------------------===// -template > -class FlowFunctions { +template +class FlowFunctions + : protected FlowFunctionTemplates { static_assert( std::is_same::value, @@ -795,6 +792,19 @@ class FlowFunctions { 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 + virtual void applyUnbalancedRetFlowFunctionSideEffects(f_t CalleeFun, + n_t ExitInst, + d_t Source) { + // By default, do nothing + } + // // Describes the data-flows alongsite a CallSite. // @@ -855,7 +865,8 @@ class FlowFunctions { // The default implementation returns a nullptr to indicate that the mechanism // should not be used. // - virtual FlowFunctionPtrType getSummaryFlowFunction(n_t Curr, f_t CalleeFun) { + virtual FlowFunctionPtrType getSummaryFlowFunction(n_t /*Curr*/, + f_t /*CalleeFun*/) { return nullptr; } }; @@ -865,7 +876,8 @@ class FlowFunctions { //////////////////////////////////////////////////////////////////////////////// template > -class Identity : public FlowFunction { +class [[deprecated("Use identityFlow() instead")]] Identity + : public FlowFunction { public: using typename FlowFunction::FlowFunctionType; using typename FlowFunction::FlowFunctionPtrType; @@ -910,7 +922,7 @@ typename FlowFunction::FlowFunctionPtrType makeLambdaFlow(Fn &&F) { } template > -class Compose : public FlowFunction { +class [[deprecated]] Compose : public FlowFunction { public: using typename FlowFunction::FlowFunctionType; using typename FlowFunction::FlowFunctionPtrType; diff --git a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h index 28bfc76a7..4cdc3610f 100644 --- a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h @@ -12,6 +12,7 @@ #include "phasar/ControlFlow/ICFGBase.h" #include "phasar/DB/ProjectIRDBBase.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" @@ -19,6 +20,7 @@ #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/NullAnalysisPrinter.h" #include "phasar/Utils/Printer.h" #include "phasar/Utils/Soundness.h" @@ -36,15 +38,31 @@ namespace psr { struct HasNoConfigurationType; +template class AllTopFnProvider { +public: + virtual ~AllTopFnProvider() = default; + /// Returns an edge function that represents the top element of the analysis. + virtual EdgeFunction allTopFunction() = 0; +}; + +template +class AllTopFnProvider< + AnalysisDomainTy, + std::enable_if_t>> { +public: + virtual ~AllTopFnProvider() = default; + /// Returns an edge function that represents the top element of the analysis. + virtual EdgeFunction allTopFunction() { + return AllTop{}; + } +}; + template > class IDETabulationProblem : public FlowFunctions, - public NodePrinter, - public DataFlowFactPrinter, - public FunctionPrinter, public EdgeFunctions, public JoinLattice, - public EdgeFactPrinter { + public AllTopFnProvider { public: using ProblemAnalysisDomain = AnalysisDomainTy; using d_t = typename AnalysisDomainTy::d_t; @@ -58,22 +76,29 @@ class IDETabulationProblem : public FlowFunctions, using ConfigurationTy = HasNoConfigurationType; - explicit IDETabulationProblem(const ProjectIRDBBase *IRDB, - std::vector EntryPoints, - std::optional ZeroValue) + explicit IDETabulationProblem( + const ProjectIRDBBase *IRDB, std::vector EntryPoints, + std::optional + ZeroValue) noexcept(std::is_nothrow_move_constructible_v) : IRDB(IRDB), EntryPoints(std::move(EntryPoints)), - ZeroValue(std::move(ZeroValue)) { + ZeroValue(std::move(ZeroValue)), + Printer(NullAnalysisPrinter::getInstance()) { assert(IRDB != nullptr); } - virtual ~IDETabulationProblem() = default; + void setAnalysisPrinter(AnalysisPrinterBase *P) { + if (P) { + Printer = P; + } else { + Printer = NullAnalysisPrinter::getInstance(); + } + } - /// Returns an edge function that represents the top element of the analysis. - virtual EdgeFunction allTopFunction() = 0; + ~IDETabulationProblem() override = default; /// Checks if the given data-flow fact is the special tautological lambda (or /// zero) fact. - [[nodiscard]] virtual bool isZeroValue(d_t FlowFact) const { + [[nodiscard]] virtual bool isZeroValue(d_t FlowFact) const noexcept { assert(ZeroValue.has_value()); return FlowFact == *ZeroValue; } @@ -83,23 +108,24 @@ class IDETabulationProblem : public FlowFunctions, [[nodiscard]] virtual InitialSeeds initialSeeds() = 0; /// Returns the special tautological lambda (or zero) fact. - [[nodiscard]] d_t getZeroValue() const { + [[nodiscard]] ByConstRef getZeroValue() const { assert(ZeroValue.has_value()); return *ZeroValue; } - void initializeZeroValue(d_t Zero) { + void initializeZeroValue(d_t Zero) noexcept( + std::is_nothrow_assignable_v &, d_t &&>) { assert(!ZeroValue.has_value()); ZeroValue = std::move(Zero); } /// Sets the configuration to be used by the IFDS/IDE solver. - void setIFDSIDESolverConfig(IFDSIDESolverConfig Config) { + void setIFDSIDESolverConfig(IFDSIDESolverConfig Config) noexcept { SolverConfig = Config; } /// Returns the configuration of the IFDS/IDE solver. - [[nodiscard]] IFDSIDESolverConfig &getIFDSIDESolverConfig() { + [[nodiscard]] IFDSIDESolverConfig &getIFDSIDESolverConfig() noexcept { return SolverConfig; } @@ -126,7 +152,8 @@ class IDETabulationProblem : public FlowFunctions, protected: typename FlowFunctions::FlowFunctionPtrType generateFromZero(d_t FactToGenerate) { - return generateFlow(std::move(FactToGenerate), getZeroValue()); + return FlowFunctions::generateFlow( + std::move(FactToGenerate), getZeroValue()); } /// Seeds that just start with ZeroValue and bottomElement() at the starting @@ -152,6 +179,8 @@ class IDETabulationProblem : public FlowFunctions, IFDSIDESolverConfig SolverConfig{}; [[maybe_unused]] Soundness SF = Soundness::Soundy; + + AnalysisPrinterBase *Printer; }; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h index e402ada1c..71dcba326 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h @@ -14,8 +14,8 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSIDESOLVERCONFIG_H_ -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSIDESOLVERCONFIG_H_ +#ifndef PHASAR_DATAFLOW_IFDSIDE_IFDSIDESOLVERCONFIG_H +#define PHASAR_DATAFLOW_IFDSIDE_IFDSIDESOLVERCONFIG_H #include "phasar/Utils/EnumFlags.h" @@ -63,9 +63,8 @@ struct IFDSIDESolverConfig { const IFDSIDESolverConfig &SC); private: - SolverConfigOptions Options = SolverConfigOptions::AutoAddZero | - SolverConfigOptions::ComputeValues | - SolverConfigOptions::RecordEdges; + SolverConfigOptions Options = + SolverConfigOptions::AutoAddZero | SolverConfigOptions::ComputeValues; }; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h index 656c80b46..e669a26a9 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h @@ -7,8 +7,8 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSTABULATIONPROBLEM_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSTABULATIONPROBLEM_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_IFDSTABULATIONPROBLEM_H +#define PHASAR_DATAFLOW_IFDSIDE_IFDSTABULATIONPROBLEM_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" @@ -76,12 +76,6 @@ class IFDSTabulationProblem d_t /*SuccNode*/) final { return EdgeIdentity{}; } - - EdgeFunction allTopFunction() final { return AllTop{}; } - - void printEdgeFact(llvm::raw_ostream &OS, BinaryDomain Val) const final { - OS << Val; - } }; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h index d12fca954..60e5ae23e 100644 --- a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h +++ b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_INITIALSEEDS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_INITIALSEEDS_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_INITIALSEEDS_H +#define PHASAR_DATAFLOW_IFDSIDE_INITIALSEEDS_H #include "phasar/Domain/BinaryDomain.h" #include "phasar/Utils/TypeTraits.h" @@ -32,7 +32,7 @@ template class InitialSeeds { InitialSeeds(const std::map> &Seeds) { for (const auto &[Node, Facts] : Seeds) { for (const auto &Fact : Facts) { - this->Seeds[Node][Fact] = BinaryDomain::TOP; + this->Seeds[Node][Fact] = BinaryDomain::BOTTOM; } } } @@ -42,11 +42,11 @@ template class InitialSeeds { template >> void addSeed(N Node, D Fact) { - addSeed(Node, Fact, BinaryDomain::TOP); + addSeed(Node, Fact, BinaryDomain::BOTTOM); } void addSeed(N Node, D Fact, L Value) { - Seeds[Node][Fact] = std::move(Value); + Seeds[std::move(Node)].insert_or_assign(std::move(Fact), std::move(Value)); } [[nodiscard]] size_t countInitialSeeds() const { diff --git a/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h b/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h new file mode 100644 index 000000000..94b98ed89 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H + +namespace psr { +enum class ESGEdgeKind { Normal, Call, CallToRet, SkipUnknownFn, Ret, Summary }; + +constexpr bool isInterProc(ESGEdgeKind Kind) noexcept { + return Kind == ESGEdgeKind::Call || Kind == ESGEdgeKind::Ret; +} + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 0db214065..637eaa083 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -7,14 +7,15 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_FLOWEDGEFUNCTIONCACHE_H_ -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_FLOWEDGEFUNCTIONCACHE_H_ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHE_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHE_H #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/Utils/EquivalenceClassMap.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseMap.h" @@ -31,6 +32,10 @@ class Value; } // namespace llvm namespace psr { + +enum class EdgeFunctionKind { Normal, Call, Return, CallToReturn, Summary }; +static constexpr size_t EdgeFunctionKindCount = 5; + template class DefaultMapKeyCompressor { public: using KeyType = KeyT; @@ -144,35 +149,35 @@ class FlowEdgeFunctionCache { AutoAddZero(Problem.getIFDSIDESolverConfig().autoAddZero()), ZV(Problem.getZeroValue()) { PAMM_GET_INSTANCE; - REG_COUNTER("Normal-FF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Normal-FF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Normal-FF Construction", 0, Full); + REG_COUNTER("Normal-FF Cache Hit", 0, Full); // Counters for the call flow functions - REG_COUNTER("Call-FF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Call-FF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Call-FF Construction", 0, Full); + REG_COUNTER("Call-FF Cache Hit", 0, Full); // Counters for return flow functions - REG_COUNTER("Return-FF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Return-FF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Return-FF Construction", 0, Full); + REG_COUNTER("Return-FF Cache Hit", 0, Full); // Counters for the call to return flow functions - REG_COUNTER("CallToRet-FF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("CallToRet-FF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("CallToRet-FF Construction", 0, Full); + REG_COUNTER("CallToRet-FF Cache Hit", 0, Full); // Counters for the summary flow functions - REG_COUNTER("Summary-FF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Summary-FF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Summary-FF Construction", 0, Full); + REG_COUNTER("Summary-FF Cache Hit", 0, Full); // Counters for the normal edge functions - REG_COUNTER("Normal-EF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Normal-EF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Normal-EF Construction", 0, Full); + REG_COUNTER("Normal-EF Cache Hit", 0, Full); // Counters for the call edge functions - REG_COUNTER("Call-EF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Call-EF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Call-EF Construction", 0, Full); + REG_COUNTER("Call-EF Cache Hit", 0, Full); // Counters for the return edge functions - REG_COUNTER("Return-EF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Return-EF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Return-EF Construction", 0, Full); + REG_COUNTER("Return-EF Cache Hit", 0, Full); // Counters for the call to return edge functions - REG_COUNTER("CallToRet-EF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("CallToRet-EF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("CallToRet-EF Construction", 0, Full); + REG_COUNTER("CallToRet-EF Cache Hit", 0, Full); // Counters for the summary edge functions - REG_COUNTER("Summary-EF Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Summary-EF Cache Hit", 0, PAMM_SEVERITY_LEVEL::Full); + REG_COUNTER("Summary-EF Construction", 0, Full); + REG_COUNTER("Summary-EF Cache Hit", 0, Full); } ~FlowEdgeFunctionCache() = default; @@ -185,16 +190,18 @@ class FlowEdgeFunctionCache { operator=(FlowEdgeFunctionCache &&FEFC) noexcept = default; FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { + assertNotNull(Curr); + assertNotNull(Succ); PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Normal flow function factory call"); - PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << Problem.NtoString(Curr)); - PHASAR_LOG_LEVEL(DEBUG, "(N) Succ Inst : " << Problem.NtoString(Succ))); + PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << NToString(Curr)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Succ Inst : " << NToString(Succ))); auto Key = createEdgeFunctionInstKey(Curr, Succ); auto SearchNormalFlowFunction = NormalFunctionCache.find(Key); if (SearchNormalFlowFunction != NormalFunctionCache.end()) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Normal-FF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Normal-FF Cache Hit", 1, Full); if (SearchNormalFlowFunction->second.FlowFuncPtr != nullptr) { return SearchNormalFlowFunction->second.FlowFuncPtr; } @@ -205,7 +212,7 @@ class FlowEdgeFunctionCache { SearchNormalFlowFunction->second.FlowFuncPtr = FF; return FF; } - INC_COUNTER("Normal-FF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Normal-FF Construction", 1, Full); auto FF = (AutoAddZero) ? std::make_shared>( Problem.getNormalFlowFunction(Curr, Succ), ZV) @@ -217,20 +224,21 @@ class FlowEdgeFunctionCache { } FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) { + assertNotNull(CallSite); + assertNotNull(DestFun); PAMM_GET_INSTANCE; - IF_LOG_ENABLED(PHASAR_LOG_LEVEL(DEBUG, "Call flow function factory call"); - PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " - << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL( - DEBUG, "(F) Dest Fun : " << Problem.FtoString(DestFun))); + IF_LOG_ENABLED( + PHASAR_LOG_LEVEL(DEBUG, "Call flow function factory call"); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(F) Dest Fun : " << FToString(DestFun))); auto Key = std::tie(CallSite, DestFun); auto SearchCallFlowFunction = CallFlowFunctionCache.find(Key); if (SearchCallFlowFunction != CallFlowFunctionCache.end()) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Call-FF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Call-FF Cache Hit", 1, Full); return SearchCallFlowFunction->second; } - INC_COUNTER("Call-FF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Call-FF Construction", 1, Full); auto FF = (AutoAddZero) ? std::make_shared>( Problem.getCallFlowFunction(CallSite, DestFun), ZV) @@ -242,25 +250,25 @@ class FlowEdgeFunctionCache { FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { + assertNotNull(CallSite); + assertNotNull(CalleeFun); + assertNotNull(ExitInst); + assertNotNull(RetSite); PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Return flow function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Site : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(F) Callee : " << Problem.FtoString(CalleeFun)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Exit Stmt : " << Problem.NtoString(ExitInst)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Ret Site : " << Problem.NtoString(RetSite))); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(F) Callee : " << FToString(CalleeFun)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Exit Stmt : " << NToString(ExitInst)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite))); auto Key = std::tie(CallSite, CalleeFun, ExitInst, RetSite); auto SearchReturnFlowFunction = ReturnFlowFunctionCache.find(Key); if (SearchReturnFlowFunction != ReturnFlowFunctionCache.end()) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("Return-FF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Return-FF Cache Hit", 1, Full); return SearchReturnFlowFunction->second; } - INC_COUNTER("Return-FF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Return-FF Construction", 1, Full); auto FF = (AutoAddZero) ? std::make_shared>( Problem.getRetFlowFunction(CallSite, CalleeFun, @@ -275,26 +283,27 @@ class FlowEdgeFunctionCache { FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, llvm::ArrayRef Callees) { + assertNotNull(CallSite); + assertNotNull(RetSite); + assertAllNotNull(Callees); PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call-to-Return flow function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Site : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Ret Site : " << Problem.NtoString(RetSite)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); PHASAR_LOG_LEVEL(DEBUG, "(F) Callee's : "); for (auto callee : Callees) { - PHASAR_LOG_LEVEL(DEBUG, " " << Problem.FtoString(callee)); + PHASAR_LOG_LEVEL(DEBUG, " " << FToString(callee)); };); auto Key = std::tie(CallSite, RetSite); auto SearchCallToRetFlowFunction = CallToRetFlowFunctionCache.find(Key); if (SearchCallToRetFlowFunction != CallToRetFlowFunctionCache.end()) { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); - INC_COUNTER("CallToRet-FF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("CallToRet-FF Cache Hit", 1, Full); return SearchCallToRetFlowFunction->second; } - INC_COUNTER("CallToRet-FF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("CallToRet-FF Construction", 1, Full); auto FF = (AutoAddZero) ? std::make_shared>( @@ -307,14 +316,14 @@ class FlowEdgeFunctionCache { } FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun) { + assertNotNull(CallSite); + assertNotNull(DestFun); // PAMM_GET_INSTANCE; - // INC_COUNTER("Summary-FF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + // INC_COUNTER("Summary-FF Construction", 1, Full); IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Summary flow function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Stmt : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(F) Dest Mthd : " << Problem.FtoString(DestFun)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(F) Dest Mthd : " << FToString(DestFun)); PHASAR_LOG_LEVEL(DEBUG, ' ')); auto FF = Problem.getSummaryFlowFunction(CallSite, DestFun); return FF; @@ -322,15 +331,16 @@ class FlowEdgeFunctionCache { EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, d_t SuccNode) { + assertNotNull(Curr); + assertNotNull(Succ); + PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Normal edge function factory call"); - PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << Problem.NtoString(Curr)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Curr Node : " << Problem.DtoString(CurrNode)); - PHASAR_LOG_LEVEL(DEBUG, "(N) Succ Inst : " << Problem.NtoString(Succ)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Succ Node : " << Problem.DtoString(SuccNode))); + PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << NToString(Curr)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Curr Node : " << DToString(CurrNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Succ Inst : " << NToString(Succ)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Succ Node : " << DToString(SuccNode))); EdgeFuncInstKey OuterMapKey = createEdgeFunctionInstKey(Curr, Succ); auto SearchInnerMap = NormalFunctionCache.find(OuterMapKey); @@ -338,13 +348,13 @@ class FlowEdgeFunctionCache { auto SearchEdgeFunc = SearchInnerMap->second.EdgeFunctionMap.find( createEdgeFunctionNodeKey(CurrNode, SuccNode)); if (SearchEdgeFunc != SearchInnerMap->second.EdgeFunctionMap.end()) { - INC_COUNTER("Normal-EF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Normal-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << SearchEdgeFunc->second); return SearchEdgeFunc->second; } - INC_COUNTER("Normal-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Normal-EF Construction", 1, Full); auto EF = Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); SearchInnerMap->second.EdgeFunctionMap.insert( @@ -354,7 +364,7 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); return EF; } - INC_COUNTER("Normal-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Normal-EF Construction", 1, Full); auto EF = Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); NormalFunctionCache.try_emplace( @@ -368,28 +378,29 @@ class FlowEdgeFunctionCache { EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, f_t DestinationFunction, d_t DestNode) { + + assertNotNull(CallSite); + assertNotNull(DestinationFunction); + PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call edge function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Stmt : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Src Node : " << Problem.DtoString(SrcNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Src Node : " << DToString(SrcNode)); - PHASAR_LOG_LEVEL( - DEBUG, "(F) Dest Fun : " << Problem.FtoString(DestinationFunction)); PHASAR_LOG_LEVEL(DEBUG, - "(D) Dest Node : " << Problem.DtoString(DestNode))); + "(F) Dest Fun : " << FToString(DestinationFunction)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Dest Node : " << DToString(DestNode))); auto Key = std::tie(CallSite, SrcNode, DestinationFunction, DestNode); auto SearchCallEdgeFunction = CallEdgeFunctionCache.find(Key); if (SearchCallEdgeFunction != CallEdgeFunctionCache.end()) { - INC_COUNTER("Call-EF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Call-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); PHASAR_LOG_LEVEL( DEBUG, "Provide Edge Function: " << SearchCallEdgeFunction->second); return SearchCallEdgeFunction->second; } - INC_COUNTER("Call-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Call-EF Construction", 1, Full); auto EF = Problem.getCallEdgeFunction(CallSite, SrcNode, DestinationFunction, DestNode); CallEdgeFunctionCache.insert(std::make_pair(Key, EF)); @@ -401,32 +412,32 @@ class FlowEdgeFunctionCache { EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, n_t ExitInst, d_t ExitNode, n_t RetSite, d_t RetNode) { + assertNotNull(CallSite); + assertNotNull(CalleeFunction); + assertNotNull(ExitInst); + assertNotNull(RetSite); + PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Return edge function factory call"); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Site : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL( - DEBUG, "(F) Callee : " << Problem.FtoString(CalleeFunction)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Exit Stmt : " << Problem.NtoString(ExitInst)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Exit Node : " << Problem.DtoString(ExitNode)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Ret Site : " << Problem.NtoString(RetSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Ret Node : " << Problem.DtoString(RetNode))); + "(F) Callee : " << FToString(CalleeFunction)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Exit Stmt : " << NToString(ExitInst)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Exit Node : " << DToString(ExitNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetNode))); auto Key = std::tie(CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); auto SearchReturnEdgeFunction = ReturnEdgeFunctionCache.find(Key); if (SearchReturnEdgeFunction != ReturnEdgeFunctionCache.end()) { - INC_COUNTER("Return-EF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Return-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); PHASAR_LOG_LEVEL( DEBUG, "Provide Edge Function: " << SearchReturnEdgeFunction->second); return SearchReturnEdgeFunction->second; } - INC_COUNTER("Return-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Return-EF Construction", 1, Full); auto EF = Problem.getReturnEdgeFunction(CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); ReturnEdgeFunctionCache.insert(std::make_pair(Key, EF)); @@ -438,22 +449,22 @@ class FlowEdgeFunctionCache { EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, d_t RetSiteNode, llvm::ArrayRef Callees) { + assertNotNull(CallSite); + assertNotNull(RetSite); + assertAllNotNull(Callees); + PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Call-to-Return edge function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Site : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Call Node : " << Problem.DtoString(CallNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Call Node : " << DToString(CallNode)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Ret Site : " << Problem.NtoString(RetSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Ret Node : " << Problem.DtoString(RetSiteNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetSiteNode)); PHASAR_LOG_LEVEL(DEBUG, "(F) Callee's : "); for (auto callee : Callees) { - PHASAR_LOG_LEVEL(DEBUG, " " << Problem.FtoString(callee)); + PHASAR_LOG_LEVEL(DEBUG, " " << FToString(callee)); }); EdgeFuncInstKey OuterMapKey = createEdgeFunctionInstKey(CallSite, RetSite); @@ -462,13 +473,13 @@ class FlowEdgeFunctionCache { auto SearchEdgeFunc = SearchInnerMap->second.find( createEdgeFunctionNodeKey(CallNode, RetSiteNode)); if (SearchEdgeFunc != SearchInnerMap->second.end()) { - INC_COUNTER("CallToRet-EF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("CallToRet-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << SearchEdgeFunc->second); return SearchEdgeFunc->second; } - INC_COUNTER("CallToRet-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("CallToRet-EF Construction", 1, Full); auto EF = Problem.getCallToRetEdgeFunction(CallSite, CallNode, RetSite, RetSiteNode, Callees); @@ -480,7 +491,7 @@ class FlowEdgeFunctionCache { return EF; } - INC_COUNTER("CallToRet-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("CallToRet-EF Construction", 1, Full); auto EF = Problem.getCallToRetEdgeFunction(CallSite, CallNode, RetSite, RetSiteNode, Callees); @@ -495,28 +506,27 @@ class FlowEdgeFunctionCache { EdgeFunction getSummaryEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, d_t RetSiteNode) { + assertNotNull(CallSite); + assertNotNull(RetSite); + PAMM_GET_INSTANCE; IF_LOG_ENABLED( PHASAR_LOG_LEVEL(DEBUG, "Summary edge function factory call"); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Call Site : " << Problem.NtoString(CallSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Call Node : " << Problem.DtoString(CallNode)); - PHASAR_LOG_LEVEL(DEBUG, - "(N) Ret Site : " << Problem.NtoString(RetSite)); - PHASAR_LOG_LEVEL(DEBUG, - "(D) Ret Node : " << Problem.DtoString(RetSiteNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Call Node : " << DToString(CallNode)); + PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); + PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetSiteNode)); PHASAR_LOG_LEVEL(DEBUG, ' ')); auto Key = std::tie(CallSite, CallNode, RetSite, RetSiteNode); auto SearchSummaryEdgeFunction = SummaryEdgeFunctionCache.find(Key); if (SearchSummaryEdgeFunction != SummaryEdgeFunctionCache.end()) { - INC_COUNTER("Summary-EF Cache Hit", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Summary-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << SearchSummaryEdgeFunction->second); return SearchSummaryEdgeFunction->second; } - INC_COUNTER("Summary-EF Construction", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Summary-EF Construction", 1, Full); auto EF = Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, RetSiteNode); SummaryEdgeFunctionCache.insert(std::make_pair(Key, EF)); @@ -597,6 +607,32 @@ class FlowEdgeFunctionCache { } } + template void foreachCachedEdgeFunction(Handler Fn) const { + for (const auto &[Key, NormalFns] : NormalFunctionCache) { + for (const auto &[Set, EF] : NormalFns.EdgeFunctionMap) { + std::invoke(Fn, EF, EdgeFunctionKind::Normal); + } + } + + for (const auto &[Key, EF] : CallEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Call); + } + + for (const auto &[Key, EF] : ReturnEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Return); + } + + for (const auto &[Key, CTRFns] : CallToRetEdgeFunctionCache) { + for (const auto &[Set, EF] : CTRFns) { + std::invoke(Fn, EF, EdgeFunctionKind::CallToReturn); + } + } + + for (const auto &[Key, EF] : SummaryEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Summary); + } + } + private: inline EdgeFuncInstKey createEdgeFunctionInstKey(n_t Lhs, n_t Rhs) { uint64_t Val = 0; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 555a69bf5..3274ced1a 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -14,21 +14,27 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IDESOLVER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IDESOLVER_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVER_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVER_H #include "phasar/Config/Configuration.h" #include "phasar/DB/ProjectIRDBBase.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionStats.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/IDESolverAPIMixin.h" #include "phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h" #include "phasar/DataFlow/IfdsIde/Solver/PathEdge.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Utils/Average.h" #include "phasar/Utils/DOTGraph.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" @@ -36,6 +42,7 @@ #include "phasar/Utils/Table.h" #include "phasar/Utils/Utilities.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -50,21 +57,17 @@ #include #include -namespace llvm { -class Instruction; -class Value; -} // namespace llvm - namespace psr { -// For sorting the results in dumpResults() -std::string getMetaDataID(const llvm::Value *V); /// Solves the given IDETabulationProblem as described in the 1996 paper by /// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results /// can then be queried by using resultAt() and resultsAt(). template > -class IDESolver { +class IDESolver + : public IDESolverAPIMixin> { + friend IDESolverAPIMixin>; + public: using ProblemTy = IDETabulationProblem; using container_type = typename ProblemTy::container_type; @@ -83,8 +86,7 @@ class IDESolver { : IDEProblem(Problem), ZeroValue(Problem.getZeroValue()), ICF(ICF), SolverConfig(Problem.getIFDSIDESolverConfig()), CachedFlowEdgeFunctions(Problem), AllTop(Problem.allTopFunction()), - JumpFn(std::make_shared>( - IDEProblem)), + JumpFn(std::make_shared>()), Seeds(Problem.initialSeeds()) { assert(ICF != nullptr); } @@ -111,77 +113,25 @@ class IDESolver { n_t Curr; for (unsigned I = 0; I < Cells.size(); ++I) { Curr = Cells[I].getRowKey(); - auto NStr = llvm::StringRef(IDEProblem.NtoString(Cells[I].getRowKey())) - .trim() - .str(); + auto NStr = + llvm::StringRef(NToString(Cells[I].getRowKey())).trim().str(); std::string NodeStr = ICF->getFunctionName(ICF->getFunctionOf(Curr)) + "::" + NStr; J[DataFlowID][NodeStr]; std::string FactStr = - llvm::StringRef(IDEProblem.DtoString(Cells[I].getColumnKey())) - .trim() - .str(); + llvm::StringRef(DToString(Cells[I].getColumnKey())).trim().str(); std::string ValueStr = - llvm::StringRef(IDEProblem.LtoString(Cells[I].getValue())) - .trim() - .str(); + llvm::StringRef(LToString(Cells[I].getValue())).trim().str(); J[DataFlowID][NodeStr]["Facts"] += {FactStr, ValueStr}; } } return J; } - /// \brief Runs the solver on the configured problem. This can take some time. - virtual void solve() { - PAMM_GET_INSTANCE; - REG_COUNTER("Gen facts", 0, PAMM_SEVERITY_LEVEL::Core); - REG_COUNTER("Kill facts", 0, PAMM_SEVERITY_LEVEL::Core); - REG_COUNTER("Summary-reuse", 0, PAMM_SEVERITY_LEVEL::Core); - REG_COUNTER("Intra Path Edges", 0, PAMM_SEVERITY_LEVEL::Core); - REG_COUNTER("Inter Path Edges", 0, PAMM_SEVERITY_LEVEL::Core); - REG_COUNTER("FF Queries", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("EF Queries", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Value Propagation", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Value Computation", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("SpecialSummary-FF Application", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("SpecialSummary-EF Queries", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("JumpFn Construction", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Process Call", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Process Normal", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("Process Exit", 0, PAMM_SEVERITY_LEVEL::Full); - REG_COUNTER("[Calls] getAliasSet", 0, PAMM_SEVERITY_LEVEL::Full); - REG_HISTOGRAM("Data-flow facts", PAMM_SEVERITY_LEVEL::Full); - REG_HISTOGRAM("Points-to", PAMM_SEVERITY_LEVEL::Full); - - PHASAR_LOG_LEVEL(INFO, "IDE solver is solving the specified problem"); - PHASAR_LOG_LEVEL(INFO, - "Submit initial seeds, construct exploded super graph"); - // computations starting here - START_TIMER("DFA Phase I", PAMM_SEVERITY_LEVEL::Full); - // We start our analysis and construct exploded supergraph - submitInitialSeeds(); - STOP_TIMER("DFA Phase I", PAMM_SEVERITY_LEVEL::Full); - if (SolverConfig.computeValues()) { - START_TIMER("DFA Phase II", PAMM_SEVERITY_LEVEL::Full); - // Computing the final values for the edge functions - PHASAR_LOG_LEVEL( - INFO, "Compute the final values according to the edge functions"); - computeValues(); - STOP_TIMER("DFA Phase II", PAMM_SEVERITY_LEVEL::Full); - } - PHASAR_LOG_LEVEL(INFO, "Problem solved"); - if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::Core) { - computeAndPrintStatistics(); - } - if (SolverConfig.emitESG()) { - emitESGAsDot(); - } - } - /// Returns the L-type result for the given value at the given statement. - [[nodiscard]] virtual l_t resultAt(n_t Stmt, d_t Value) { - return ValTab.get(Stmt, Value); + [[nodiscard]] l_t resultAt(n_t Stmt, d_t Value) { + return getSolverResults().resultAt(Stmt, Value); } /// Returns the L-type result at the given statement for the given data-flow @@ -203,11 +153,7 @@ class IDESolver { [[nodiscard]] typename std::enable_if_t< std::is_same_v, llvm::Instruction *>, l_t> resultAtInLLVMSSA(NTy Stmt, d_t Value) { - if (Stmt->getType()->isVoidTy()) { - return ValTab.get(Stmt, Value); - } - assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); - return ValTab.get(Stmt->getNextNode(), Value); + return getSolverResults().resultAtInLLVMSSA(Stmt, Value); } /// Returns the resulting environment for the given statement. @@ -215,17 +161,7 @@ class IDESolver { /// TOP values are never returned. [[nodiscard]] virtual std::unordered_map resultsAt(n_t Stmt, bool StripZero = false) /*TODO const*/ { - std::unordered_map Result = ValTab.row(Stmt); - if (StripZero) { - for (auto It = Result.begin(); It != Result.end();) { - if (IDEProblem.isZeroValue(It->first)) { - It = Result.erase(It); - } else { - ++It; - } - } - } - return Result; + return getSolverResults().resultsAt(Stmt, StripZero); } /// Returns the data-flow results at the given statement while respecting @@ -248,23 +184,7 @@ class IDESolver { std::is_same_v, llvm::Instruction *>, std::unordered_map> resultsAtInLLVMSSA(NTy Stmt, bool StripZero = false) { - std::unordered_map Result = [this, Stmt]() { - if (Stmt->getType()->isVoidTy()) { - return ValTab.row(Stmt); - } - return ValTab.row(Stmt->getNextNode()); - }(); - if (StripZero) { - // TODO: replace with std::erase_if (C++20) - for (auto It = Result.begin(); It != Result.end();) { - if (IDEProblem.isZeroValue(It->first)) { - It = Result.erase(It); - } else { - ++It; - } - } - } - return Result; + return getSolverResults().resultsAtInLLVMSSA(Stmt, StripZero); } virtual void emitTextReport(llvm::raw_ostream &OS = llvm::outs()) { @@ -276,49 +196,7 @@ class IDESolver { } void dumpResults(llvm::raw_ostream &OS = llvm::outs()) { - PAMM_GET_INSTANCE; - START_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); - OS << "\n***************************************************************\n" - << "* Raw IDESolver results *\n" - << "***************************************************************\n"; - auto Cells = this->ValTab.cellVec(); - if (Cells.empty()) { - OS << "No results computed!" << '\n'; - } else { - std::sort( - Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { - if constexpr (std::is_same_v) { - return StringIDLess{}(getMetaDataID(Lhs.getRowKey()), - getMetaDataID(Rhs.getRowKey())); - } else { - // If non-LLVM IR is used - return Lhs.getRowKey() < Rhs.getRowKey(); - } - }); - n_t Prev = n_t{}; - n_t Curr = n_t{}; - f_t PrevFn = f_t{}; - f_t CurrFn = f_t{}; - for (unsigned I = 0; I < Cells.size(); ++I) { - Curr = Cells[I].getRowKey(); - CurrFn = ICF->getFunctionOf(Curr); - if (PrevFn != CurrFn) { - PrevFn = CurrFn; - OS << "\n\n============ Results for function '" + - ICF->getFunctionName(CurrFn) + "' ============\n"; - } - if (Prev != Curr) { - Prev = Curr; - std::string NString = IDEProblem.NtoString(Curr); - std::string Line(NString.size(), '-'); - OS << "\n\nN: " << NString << "\n---" << Line << '\n'; - } - OS << "\tD: " << IDEProblem.DtoString(Cells[I].getColumnKey()) - << " | V: " << IDEProblem.LtoString(Cells[I].getValue()) << '\n'; - } - } - OS << '\n'; - STOP_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); + getSolverResults().dumpResults(*ICF, OS); } void dumpAllInterPathEdges() { @@ -361,50 +239,102 @@ class IDESolver { } } - SolverResults getSolverResults() { - return SolverResults(this->ValTab, - IDEProblem.getZeroValue()); + /// Returns a view into the computed solver-results. + /// + /// NOTE: The SolverResults store a reference into this IDESolver, so its + /// lifetime is also bound to the lifetime of this solver. If you want to use + /// the solverResults beyond the lifetime of this solver, use + /// comsumeSolverResults() instead. + [[nodiscard]] SolverResults getSolverResults() noexcept { + return SolverResults(this->ValTab, ZeroValue); } -protected: - // have a shared point to allow for a copy constructor of IDESolver - IDETabulationProblem &IDEProblem; - d_t ZeroValue; - const i_t *ICF; - IFDSIDESolverConfig &SolverConfig; - unsigned PathEdgeCount = 0; - - FlowEdgeFunctionCache CachedFlowEdgeFunctions; - - Table> ComputedIntraPathEdges; + /// Moves the computed solver-results out of this solver such that the solver + /// can be destroyed without that the analysis results are lost. + /// Do not call any function (including getSolverResults()) on this IDESolver + /// instance after that. + [[nodiscard]] OwningSolverResults + consumeSolverResults() noexcept(std::is_nothrow_move_constructible_v) { + return OwningSolverResults(std::move(this->ValTab), + std::move(ZeroValue)); + } - Table> ComputedInterPathEdges; + [[nodiscard]] EdgeFunctionStats getEdgeFunctionStatistics() const { + detail::EdgeFunctionStatsData Stats{}; + + // Cached Edge Functions + { + std::array>, EdgeFunctionKindCount> + UniqueEFs{}; + Sampler DepthSampler{}; + Sampler UniqueDepthSampler{}; + // TODO: Cache EFs + CachedFlowEdgeFunctions.foreachCachedEdgeFunction( + [&](EdgeFunction EF, EdgeFunctionKind Kind) { + auto Depth = EF.depth(); + DepthSampler.addSample(Depth); + if (Depth > Stats.MaxDepth) { + Stats.MaxDepth = Depth; + } - EdgeFunction AllTop; + if (UniqueEFs[size_t(Kind)].insert(std::move(EF)).second) { + UniqueDepthSampler.addSample(Depth); + } + Stats.TotalEFCount[size_t(Kind)]++; + Stats.PerAllocCount[size_t(EF.getAllocationPolicy())]++; + }); - std::shared_ptr> JumpFn; + size_t TotalUniqueNumEF = 0; + for (size_t I = 0, End = UniqueEFs.size(); I != End; ++I) { + Stats.UniqueEFCount[I] = UniqueEFs[I].size(); // NOLINT + TotalUniqueNumEF += UniqueEFs[I].size(); + } - std::map, std::vector>> - IntermediateEdgeFunctions; + Stats.AvgDepth = DepthSampler.getAverage(); + Stats.AvgUniqueDepth = UniqueDepthSampler.getAverage(); + } - // stores summaries that were queried before they were computed - // see CC 2010 paper by Naeem, Lhotak and Rodriguez - Table>> EndsummaryTab; + // Jump Functions + { + llvm::DenseSet> UniqueJumpFns; + llvm::DenseSet AllocatedJumpFns; + Sampler DepthSampler{}; + Sampler UniqueDepthSampler{}; + Sampler AllocDepthSampler{}; + + JumpFn->foreachEdgeFunction([&](EdgeFunction JF) { + ++Stats.TotalNumJF; + auto Depth = JF.depth(); + DepthSampler.addSample(Depth); + if (Depth > Stats.MaxJFDepth) { + Stats.MaxJFDepth = Depth; + } - // edges going along calls - // see CC 2010 paper by Naeem, Lhotak and Rodriguez - Table> IncomingTab; + if (AllocatedJumpFns.insert(JF.getOpaqueValue()).second) { + AllocDepthSampler.addSample(Depth); + } - // stores the return sites (inside callers) to which we have unbalanced - // returns if SolverConfig.followReturnPastSeeds is enabled - std::set UnbalancedRetSites; + Stats.PerAllocJFCount[size_t(JF.getAllocationPolicy())]++; - InitialSeeds Seeds; + if (UniqueJumpFns.insert(std::move(JF)).second) { + UniqueDepthSampler.addSample(Depth); + } + }); - Table ValTab; + Stats.UniqueNumJF = UniqueJumpFns.size(); + Stats.NumJFObjects = AllocatedJumpFns.size(); + Stats.AvgJFDepth = DepthSampler.getAverage(); + Stats.AvgUniqueJFDepth = UniqueDepthSampler.getAverage(); + Stats.AvgJFObjDepth = AllocDepthSampler.getAverage(); + } + return Stats; + } - std::map, size_t> FSummaryReuse; + void printEdgeFunctionStatistics(llvm::raw_ostream &OS = llvm::outs()) const { + OS << getEdgeFunctionStatistics() << '\n'; + } +protected: /// Lines 13-20 of the algorithm; processing a call site in the caller's /// context. /// @@ -424,9 +354,9 @@ class IDESolver { /// virtual void processCall(const PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Call", 1, PAMM_SEVERITY_LEVEL::Full); - PHASAR_LOG_LEVEL(DEBUG, "Process call at target: " - << IDEProblem.NtoString(Edge.getTarget())); + INC_COUNTER("Process Call", 1, Full); + PHASAR_LOG_LEVEL(DEBUG, + "Process call at target: " << NToString(Edge.getTarget())); d_t d1 = Edge.factAtSource(); n_t n = Edge.getTarget(); // a call node; line 14... @@ -435,54 +365,57 @@ class IDESolver { const auto &ReturnSiteNs = ICF->getReturnSitesOfCallAt(n); const auto &Callees = ICF->getCalleesOfCallAt(n); - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL(DEBUG, "Possible callees:"); for (auto Callee - : Callees) { - PHASAR_LOG_LEVEL(DEBUG, " " << Callee->getName()); - } PHASAR_LOG_LEVEL(DEBUG, "Possible return sites:"); - for (auto ret - : ReturnSiteNs) { - PHASAR_LOG_LEVEL(DEBUG, " " << IDEProblem.NtoString(ret)); - }); + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL(DEBUG, "Possible callees:"); + for (auto Callee : Callees) { + PHASAR_LOG_LEVEL(DEBUG, " " << Callee->getName()); + } + PHASAR_LOG_LEVEL(DEBUG, "Possible return sites:"); + for (auto ret : ReturnSiteNs) { + PHASAR_LOG_LEVEL(DEBUG, " " << NToString(ret)); + } + }); + + bool HasNoCalleeInformation = true; // for each possible callee for (f_t SCalledProcN : Callees) { // still line 14 // check if a special summary for the called procedure exists FlowFunctionPtrType SpecialSum = CachedFlowEdgeFunctions.getSummaryFlowFunction(n, SCalledProcN); + // if a special summary is available, treat this as a normal flow // and use the summary flow and edge functions + if (SpecialSum) { + HasNoCalleeInformation = false; PHASAR_LOG_LEVEL(DEBUG, "Found and process special summary"); for (n_t ReturnSiteN : ReturnSiteNs) { container_type Res = computeSummaryFlowFunction(SpecialSum, d1, d2); - INC_COUNTER("SpecialSummary-FF Application", 1, - PAMM_SEVERITY_LEVEL::Full); - ADD_TO_HISTOGRAM("Data-flow facts", res.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(n, ReturnSiteN, d2, Res, false); + INC_COUNTER("SpecialSummary-FF Application", 1, Full); + ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); + saveEdges(n, ReturnSiteN, d2, Res, ESGEdgeKind::Summary); for (d_t d3 : Res) { EdgeFunction SumEdgFnE = CachedFlowEdgeFunctions.getSummaryEdgeFunction(n, d2, ReturnSiteN, d3); - INC_COUNTER("SpecialSummary-EF Queries", 1, - PAMM_SEVERITY_LEVEL::Full); - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL( - DEBUG, "Queried Summary Edge Function: " << SumEdgFnE); - PHASAR_LOG_LEVEL(DEBUG, "Compose: " << SumEdgFnE << " * " << f - << '\n')); - propagate(d1, ReturnSiteN, d3, f.composeWith(SumEdgFnE), n, false); + INC_COUNTER("SpecialSummary-EF Queries", 1, Full); + + PHASAR_LOG_LEVEL(DEBUG, + "Queried Summary Edge Function: " << SumEdgFnE); + PHASAR_LOG_LEVEL(DEBUG, + "Compose: " << SumEdgFnE << " * " << f << '\n'); + WorkList.emplace_back(PathEdge(d1, ReturnSiteN, std::move(d3)), + f.composeWith(SumEdgFnE)); } } } else { // compute the call-flow function FlowFunctionPtrType Function = CachedFlowEdgeFunctions.getCallFlowFunction(n, SCalledProcN); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); container_type Res = computeCallFlowFunction(Function, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", res.size(), 1, - PAMM_SEVERITY_LEVEL::Full); + ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); // for each callee's start point(s) auto StartPointsOf = ICF->getStartPointsOf(SCalledProcN); if (StartPointsOf.empty()) { @@ -492,25 +425,26 @@ class IDESolver { } // if startPointsOf is empty, the called function is a declaration for (n_t SP : StartPointsOf) { - saveEdges(n, SP, d2, Res, true); + HasNoCalleeInformation = false; + saveEdges(n, SP, d2, Res, ESGEdgeKind::Call); // for each result node of the call-flow function for (d_t d3 : Res) { using TableCell = typename Table>::Cell; // create initial self-loop - PHASAR_LOG_LEVEL(DEBUG, "Create initial self-loop with D: " - << IDEProblem.DtoString(d3)); - propagate(d3, SP, d3, EdgeIdentity{}, n, - false); // line 15 - // register the fact that has an incoming edge from - // line 15.1 of Naeem/Lhotak/Rodriguez + PHASAR_LOG_LEVEL( + DEBUG, "Create initial self-loop with D: " << DToString(d3)); + WorkList.emplace_back(PathEdge(d3, SP, d3), + EdgeIdentity{}); // line 15 + // register the fact that has an incoming edge from + // line 15.1 of Naeem/Lhotak/Rodriguez addIncoming(SP, d3, n, d2); // line 15.2, copy to avoid concurrent modification exceptions by // other threads // const std::set endSumm(endSummary(sP, d3)); // llvm::outs() << "ENDSUMM" << '\n'; // llvm::outs() << "Size: " << endSumm.size() << '\n'; - // llvm::outs() << "sP: " << IDEProblem.NtoString(sP) - // << "\nd3: " << IDEProblem.DtoString(d3) + // llvm::outs() << "sP: " << NToString(sP) + // << "\nd3: " << DToString(d3) // << '\n'; // printEndSummaryTab(); // still line 15.2 of Naeem/Lhotak/Rodriguez @@ -528,12 +462,12 @@ class IDESolver { FlowFunctionPtrType RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction(n, SCalledProcN, eP, RetSiteN); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); const container_type ReturnedFacts = computeReturnFlowFunction( RetFunction, d3, d4, n, Container{d2}); - ADD_TO_HISTOGRAM("Data-flow facts", returnedFacts.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(eP, RetSiteN, d4, ReturnedFacts, true); + ADD_TO_HISTOGRAM("Data-flow facts", ReturnedFacts.size(), 1, + Full); + saveEdges(eP, RetSiteN, d4, ReturnedFacts, ESGEdgeKind::Ret); // for each target value of the function for (d_t d5 : ReturnedFacts) { // update the caller-side summary function @@ -557,7 +491,7 @@ class IDESolver { d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 2, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("EF Queries", 2, Full); // compose call * calleeSummary * return edge functions PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << fCalleeSummary << " * " @@ -570,8 +504,9 @@ class IDESolver { d_t d5_restoredCtx = restoreContextOnReturnedFact(n, d2, d5); // propagte the effects of the entire call PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f); - propagate(d1, RetSiteN, d5_restoredCtx, f.composeWith(fPrime), - n, false); + WorkList.emplace_back( + PathEdge(d1, RetSiteN, std::move(d5_restoredCtx)), + f.composeWith(fPrime)); } } } @@ -585,12 +520,13 @@ class IDESolver { FlowFunctionPtrType CallToReturnFF = CachedFlowEdgeFunctions.getCallToRetFlowFunction(n, ReturnSiteN, Callees); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); container_type ReturnFacts = computeCallToReturnFlowFunction(CallToReturnFF, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", returnFacts.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(n, ReturnSiteN, d2, ReturnFacts, false); + ADD_TO_HISTOGRAM("Data-flow facts", ReturnFacts.size(), 1, Full); + saveEdges(n, ReturnSiteN, d2, ReturnFacts, + HasNoCalleeInformation ? ESGEdgeKind::SkipUnknownFn + : ESGEdgeKind::CallToRet); for (d_t d3 : ReturnFacts) { EdgeFunction EdgeFnE = CachedFlowEdgeFunctions.getCallToRetEdgeFunction(n, d2, ReturnSiteN, @@ -601,11 +537,12 @@ class IDESolver { IntermediateEdgeFunctions[std::make_tuple(n, d2, ReturnSiteN, d3)] .push_back(EdgeFnE); } - INC_COUNTER("EF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("EF Queries", 1, Full); auto fPrime = f.composeWith(EdgeFnE); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << EdgeFnE << " * " << f << " = " << fPrime); - propagate(d1, ReturnSiteN, d3, fPrime, n, false); + WorkList.emplace_back(PathEdge(d1, ReturnSiteN, std::move(d3)), + std::move(fPrime)); } } } @@ -614,23 +551,21 @@ class IDESolver { /// Simply propagate normal, intra-procedural flows. /// @param edge /// - virtual void processNormalFlow(const PathEdge Edge) { + virtual void processNormalFlow(PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Normal", 1, PAMM_SEVERITY_LEVEL::Full); - PHASAR_LOG_LEVEL(DEBUG, "Process normal at target: " - << IDEProblem.NtoString(Edge.getTarget())); - d_t d1 = Edge.factAtSource(); - n_t n = Edge.getTarget(); - d_t d2 = Edge.factAtTarget(); + INC_COUNTER("Process Normal", 1, Full); + PHASAR_LOG_LEVEL( + DEBUG, "Process normal at target: " << NToString(Edge.getTarget())); EdgeFunction f = jumpFunction(Edge); + auto [d1, n, d2] = Edge.consume(); + for (const auto nPrime : ICF->getSuccsOf(n)) { FlowFunctionPtrType FlowFunc = CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); const container_type Res = computeNormalFlowFunction(FlowFunc, d1, d2); - ADD_TO_HISTOGRAM("Data-flow facts", res.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(n, nPrime, d2, Res, false); + ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); + saveEdges(n, nPrime, d2, Res, ESGEdgeKind::Normal); for (d_t d3 : Res) { EdgeFunction g = CachedFlowEdgeFunctions.getNormalEdgeFunction(n, d2, nPrime, d3); @@ -642,8 +577,9 @@ class IDESolver { } PHASAR_LOG_LEVEL(DEBUG, "Compose: " << g << " * " << f << " = " << fPrime); - INC_COUNTER("EF Queries", 1, PAMM_SEVERITY_LEVEL::Full); - propagate(d1, nPrime, d3, fPrime, nullptr, false); + INC_COUNTER("EF Queries", 1, Full); + WorkList.emplace_back(PathEdge(d1, nPrime, std::move(d3)), + std::move(fPrime)); } } } @@ -663,7 +599,7 @@ class IDESolver { auto fPrime = Entry.second; n_t SP = Stmt; l_t Val = val(SP, Fact); - INC_COUNTER("Value Propagation", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Value Propagation", 1, Full); propagateValue(CallSite, dPrime, fPrime.computeTarget(Val)); } } @@ -675,7 +611,7 @@ class IDESolver { for (const f_t Callee : ICF->getCalleesOfCallAt(Stmt)) { FlowFunctionPtrType CallFlowFunction = CachedFlowEdgeFunctions.getCallFlowFunction(Stmt, Callee); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); for (const d_t dPrime : CallFlowFunction->computeTargets(Fact)) { EdgeFunction EdgeFn = CachedFlowEdgeFunctions.getCallEdgeFunction( Stmt, Fact, Callee, dPrime); @@ -686,9 +622,9 @@ class IDESolver { .push_back(EdgeFn); } } - INC_COUNTER("EF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("EF Queries", 1, Full); for (const n_t StartPoint : ICF->getStartPointsOf(Callee)) { - INC_COUNTER("Value Propagation", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Value Propagation", 1, Full); propagateValue(StartPoint, dPrime, EdgeFn.computeTarget(val(Stmt, Fact))); } @@ -701,7 +637,7 @@ class IDESolver { l_t LPrime = joinValueAt(NHashN, NHashD, ValNHash, L); if (!(LPrime == ValNHash)) { setVal(NHashN, NHashD, std::move(LPrime)); - valuePropagationTask(std::pair(NHashN, NHashD)); + ValuePropWL.emplace_back(std::move(NHashN), std::move(NHashD)); } } @@ -714,12 +650,12 @@ class IDESolver { } void setVal(n_t NHashN, d_t NHashD, l_t L) { - IF_LOG_ENABLED({ + IF_LOG_LEVEL_ENABLED(DEBUG, { PHASAR_LOG_LEVEL(DEBUG, "Function : " << ICF->getFunctionOf(NHashN)->getName()); - PHASAR_LOG_LEVEL(DEBUG, "Inst. : " << IDEProblem.NtoString(NHashN)); - PHASAR_LOG_LEVEL(DEBUG, "Fact : " << IDEProblem.DtoString(NHashD)); - PHASAR_LOG_LEVEL(DEBUG, "Value : " << IDEProblem.LtoString(L)); + PHASAR_LOG_LEVEL(DEBUG, "Inst. : " << NToString(NHashN)); + PHASAR_LOG_LEVEL(DEBUG, "Fact : " << DToString(NHashD)); + PHASAR_LOG_LEVEL(DEBUG, "Value : " << LToString(L)); PHASAR_LOG_LEVEL(DEBUG, ' '); }); // TOP is the implicit default value which we do not need to store. @@ -732,14 +668,14 @@ class IDESolver { } EdgeFunction jumpFunction(const PathEdge Edge) { - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL(DEBUG, "JumpFunctions Forward-Lookup:"); - PHASAR_LOG_LEVEL(DEBUG, " Source D: " << IDEProblem.DtoString( - Edge.factAtSource())); - PHASAR_LOG_LEVEL( - DEBUG, " Target N: " << IDEProblem.NtoString(Edge.getTarget())); - PHASAR_LOG_LEVEL(DEBUG, " Target D: " << IDEProblem.DtoString( - Edge.factAtTarget()))); + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL(DEBUG, "JumpFunctions Forward-Lookup:"); + PHASAR_LOG_LEVEL(DEBUG, + " Source D: " << DToString(Edge.factAtSource())); + PHASAR_LOG_LEVEL(DEBUG, " Target N: " << NToString(Edge.getTarget())); + PHASAR_LOG_LEVEL(DEBUG, + " Target D: " << DToString(Edge.factAtTarget())); + }); auto FwdLookupRes = JumpFn->forwardLookup(Edge.factAtSource(), Edge.getTarget()); @@ -767,42 +703,40 @@ class IDESolver { } // should be made a callable at some point - void pathEdgeProcessingTask(const PathEdge Edge) { + void pathEdgeProcessingTask(PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("JumpFn Construction", 1, PAMM_SEVERITY_LEVEL::Full); - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL( - DEBUG, - "-------------------------------------------- " - << PathEdgeCount - << ". Path Edge --------------------------------------------"); - PHASAR_LOG_LEVEL(DEBUG, ' '); - PHASAR_LOG_LEVEL(DEBUG, "Process " << PathEdgeCount << ". path edge:"); - PHASAR_LOG_LEVEL(DEBUG, "< D source: " - << IDEProblem.DtoString(Edge.factAtSource()) - << " ;"); - PHASAR_LOG_LEVEL(DEBUG, " N target: " - << IDEProblem.NtoString(Edge.getTarget()) - << " ;"); - PHASAR_LOG_LEVEL(DEBUG, " D target: " - << IDEProblem.DtoString(Edge.factAtTarget()) - << " >"); - PHASAR_LOG_LEVEL(DEBUG, ' ')); + INC_COUNTER("JumpFn Construction", 1, Full); + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL( + DEBUG, + "-------------------------------------------- " + << PathEdgeCount + << ". Path Edge --------------------------------------------"); + PHASAR_LOG_LEVEL(DEBUG, ' '); + PHASAR_LOG_LEVEL(DEBUG, "Process " << PathEdgeCount << ". path edge:"); + PHASAR_LOG_LEVEL(DEBUG, "< D source: " << DToString(Edge.factAtSource()) + << " ;"); + PHASAR_LOG_LEVEL(DEBUG, + " N target: " << NToString(Edge.getTarget()) << " ;"); + PHASAR_LOG_LEVEL(DEBUG, " D target: " << DToString(Edge.factAtTarget()) + << " >"); + PHASAR_LOG_LEVEL(DEBUG, ' '); + }); if (!ICF->isCallSite(Edge.getTarget())) { if (ICF->isExitInst(Edge.getTarget())) { processExit(Edge); } if (!ICF->getSuccsOf(Edge.getTarget()).empty()) { - processNormalFlow(Edge); + processNormalFlow(std::move(Edge)); } } else { - processCall(Edge); + processCall(std::move(Edge)); } } // should be made a callable at some point - void valuePropagationTask(const std::pair NAndD) { + void valuePropagationTask(std::pair NAndD) { n_t n = NAndD.first; // our initial seeds are not necessarily method-start points but here they // should be treated as such the same also for unbalanced return sites in @@ -836,27 +770,24 @@ class IDESolver { setVal(n, d, IDEProblem.join(val(n, d), fPrime.computeTarget(std::move(TargetVal)))); - INC_COUNTER("Value Computation", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("Value Computation", 1, Full); } } } } virtual void saveEdges(n_t SourceNode, n_t SinkStmt, d_t SourceVal, - const container_type &DestVals, bool InterP) { + const container_type &DestVals, ESGEdgeKind Kind) { if (!SolverConfig.recordEdges()) { return; } Table> &TgtMap = - (InterP) ? ComputedInterPathEdges : ComputedIntraPathEdges; + (isInterProc(Kind)) ? ComputedInterPathEdges : ComputedIntraPathEdges; TgtMap.get(SourceNode, SinkStmt)[SourceVal].insert(DestVals.begin(), DestVals.end()); } - /// Computes the final values for edge functions. - void computeValues() { - PHASAR_LOG_LEVEL(DEBUG, "Start computing values"); - // Phase II(i) + void submitInitialValues() { std::map> AllSeeds = Seeds.getSeeds(); for (n_t UnbalancedRetSite : UnbalancedRetSites) { if (AllSeeds.find(UnbalancedRetSite) == AllSeeds.end()) { @@ -866,18 +797,33 @@ class IDESolver { // do processing for (const auto &[StartPoint, Facts] : AllSeeds) { for (auto &[Fact, Value] : Facts) { - PHASAR_LOG_LEVEL(DEBUG, - "set initial seed at: " - << IDEProblem.NtoString(StartPoint) - << ", fact: " << IDEProblem.DtoString(Fact) - << ", value: " << IDEProblem.LtoString(Value)); + PHASAR_LOG_LEVEL(DEBUG, "set initial seed at: " + << NToString(StartPoint) + << ", fact: " << DToString(Fact) + << ", value: " << LToString(Value)); // initialize the initial seeds with the top element as we have no // information at the beginning of the value computation problem - setVal(StartPoint, Fact, Value); + l_t OldVal = val(StartPoint, Fact); + auto NewVal = IDEProblem.join(Value, std::move(OldVal)); + setVal(StartPoint, Fact, std::move(NewVal)); + std::pair SuperGraphNode(StartPoint, Fact); - valuePropagationTask(SuperGraphNode); + valuePropagationTask(std::move(SuperGraphNode)); } } + } + + /// Computes the final values for edge functions. + void computeValues() { + PHASAR_LOG_LEVEL(DEBUG, "Start computing values"); + // Phase II(i) + submitInitialValues(); + while (!ValuePropWL.empty()) { + auto NAndD = std::move(ValuePropWL.back()); + ValuePropWL.pop_back(); + valuePropagationTask(std::move(NAndD)); + } + // Phase II(ii) // we create an array of all nodes and then dispatch fractions of this // array to multiple threads @@ -898,7 +844,7 @@ class IDESolver { // Add zero value if it's not in the set of facts. PHASAR_LOG_LEVEL( DEBUG, "Zero-Value has been added automatically to start point: " - << IDEProblem.NtoString(StartPoint)); + << NToString(StartPoint)); Seeds.addSeed(StartPoint, ZeroValue, IDEProblem.bottomElement()); } } @@ -906,27 +852,25 @@ class IDESolver { "Number of initial seeds: " << Seeds.countInitialSeeds()); PHASAR_LOG_LEVEL(DEBUG, "List of initial seeds: "); for (const auto &[StartPoint, Facts] : Seeds.getSeeds()) { - PHASAR_LOG_LEVEL(DEBUG, - "Start point: " << IDEProblem.NtoString(StartPoint)); + PHASAR_LOG_LEVEL(DEBUG, "Start point: " << NToString(StartPoint)); /// If statically disabling the logger, Fact and Value are unused. To /// prevent the copilation to fail with -Werror, add the [[maybe_unused]] /// attribute for ([[maybe_unused]] const auto &[Fact, Value] : Facts) { - PHASAR_LOG_LEVEL(DEBUG, "\tFact: " << IDEProblem.DtoString(Fact)); - PHASAR_LOG_LEVEL(DEBUG, "\tValue: " << IDEProblem.LtoString(Value)); + PHASAR_LOG_LEVEL(DEBUG, "\tFact: " << DToString(Fact)); + PHASAR_LOG_LEVEL(DEBUG, "\tValue: " << LToString(Value)); } } for (const auto &[StartPoint, Facts] : Seeds.getSeeds()) { for (const auto &[Fact, Value] : Facts) { - PHASAR_LOG_LEVEL( - DEBUG, "Submit seed at: " << IDEProblem.NtoString(StartPoint)); - PHASAR_LOG_LEVEL(DEBUG, "\tFact: " << IDEProblem.DtoString(Fact)); - PHASAR_LOG_LEVEL(DEBUG, "\tValue: " << IDEProblem.LtoString(Value)); + PHASAR_LOG_LEVEL(DEBUG, "Submit seed at: " << NToString(StartPoint)); + PHASAR_LOG_LEVEL(DEBUG, "\tFact: " << DToString(Fact)); + PHASAR_LOG_LEVEL(DEBUG, "\tValue: " << LToString(Value)); if (!IDEProblem.isZeroValue(Fact)) { - INC_COUNTER("Gen facts", 1, PAMM_SEVERITY_LEVEL::Core); + INC_COUNTER("Gen facts", 1, Core); } - propagate(Fact, StartPoint, Fact, EdgeIdentity{}, nullptr, false); - JumpFn->addFunction(Fact, StartPoint, Fact, EdgeIdentity{}); + WorkList.emplace_back(PathEdge(Fact, StartPoint, Fact), + EdgeIdentity{}); } } } @@ -941,9 +885,9 @@ class IDESolver { /// virtual void processExit(const PathEdge Edge) { PAMM_GET_INSTANCE; - INC_COUNTER("Process Exit", 1, PAMM_SEVERITY_LEVEL::Full); - PHASAR_LOG_LEVEL(DEBUG, "Process exit at target: " - << IDEProblem.NtoString(Edge.getTarget())); + INC_COUNTER("Process Exit", 1, Full); + PHASAR_LOG_LEVEL(DEBUG, + "Process exit at target: " << NToString(Edge.getTarget())); n_t n = Edge.getTarget(); // an exit node; line 21... EdgeFunction f = jumpFunction(Edge); f_t FunctionThatNeedsSummary = ICF->getFunctionOf(n); @@ -973,14 +917,13 @@ class IDESolver { FlowFunctionPtrType RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( c, FunctionThatNeedsSummary, n, RetSiteC); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); // for each incoming-call value for (d_t d4 : Entry.second) { const container_type Targets = computeReturnFlowFunction(RetFunction, d1, d2, c, Entry.second); - ADD_TO_HISTOGRAM("Data-flow facts", targets.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(n, RetSiteC, d2, Targets, true); + ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); + saveEdges(n, RetSiteC, d2, Targets, ESGEdgeKind::Ret); // for each target value at the return site // line 23 for (d_t d5 : Targets) { @@ -1002,7 +945,7 @@ class IDESolver { IntermediateEdgeFunctions[std::make_tuple(n, d2, RetSiteC, d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 2, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("EF Queries", 2, Full); // compose call function * function * return function PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f << " * " << f4); @@ -1020,8 +963,9 @@ class IDESolver { d_t d3 = ValAndFunc.first; d_t d5_restoredCtx = restoreContextOnReturnedFact(c, d4, d5); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f3); - propagate(d3, RetSiteC, d5_restoredCtx, - f3.composeWith(fPrime), c, false); + WorkList.emplace_back(PathEdge(std::move(d3), RetSiteC, + std::move(d5_restoredCtx)), + f3.composeWith(fPrime)); } } } @@ -1035,20 +979,20 @@ class IDESolver { // conditionally generated values should only // be propagated into callers that have an incoming edge for this // condition - if (SolverConfig.followReturnsPastSeeds() && Inc.empty() && - IDEProblem.isZeroValue(d1)) { + /// TODO: Add a check for "d1 is seed in functionOf(n)" + if (SolverConfig.followReturnsPastSeeds() && Inc.empty() /*&& + IDEProblem.isZeroValue(d1)*/) { const auto &Callers = ICF->getCallersOf(FunctionThatNeedsSummary); for (n_t Caller : Callers) { for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(Caller)) { FlowFunctionPtrType RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( Caller, FunctionThatNeedsSummary, n, RetSiteC); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("FF Queries", 1, Full); const container_type Targets = computeReturnFlowFunction( RetFunction, d1, d2, Caller, Container{ZeroValue}); - ADD_TO_HISTOGRAM("Data-flow facts", targets.size(), 1, - PAMM_SEVERITY_LEVEL::Full); - saveEdges(n, RetSiteC, d2, Targets, true); + ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); + saveEdges(n, RetSiteC, d2, Targets, ESGEdgeKind::Ret); for (d_t d5 : Targets) { EdgeFunction f5 = CachedFlowEdgeFunctions.getReturnEdgeFunction( @@ -1058,7 +1002,7 @@ class IDESolver { IntermediateEdgeFunctions[std::make_tuple(n, d2, RetSiteC, d5)] .push_back(f5); } - INC_COUNTER("EF Queries", 1, PAMM_SEVERITY_LEVEL::Full); + INC_COUNTER("EF Queries", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f); propagteUnbalancedReturnFlow(RetSiteC, d5, f.composeWith(f5), Caller); @@ -1072,20 +1016,18 @@ class IDESolver { // the flow function has a side effect such as registering a taint; // instead we thus call the return flow function will a null caller if (Callers.empty()) { - FlowFunctionPtrType RetFunction = - CachedFlowEdgeFunctions.getRetFlowFunction( - nullptr, FunctionThatNeedsSummary, n, nullptr); - INC_COUNTER("FF Queries", 1, PAMM_SEVERITY_LEVEL::Full); - RetFunction->computeTargets(d2); + IDEProblem.applyUnbalancedRetFlowFunctionSideEffects( + FunctionThatNeedsSummary, n, d2); } } } void propagteUnbalancedReturnFlow(n_t RetSiteC, d_t TargetVal, EdgeFunction EdgeFunc, - n_t RelatedCallSite) { - propagate(ZeroValue, RetSiteC, TargetVal, std::move(EdgeFunc), - RelatedCallSite, true); + n_t /*RelatedCallSite*/) { + WorkList.emplace_back( + PathEdge(ZeroValue, std::move(RetSiteC), std::move(TargetVal)), + std::move(EdgeFunc)); } /// This method will be called for each incoming edge and can be used to @@ -1188,17 +1130,11 @@ class IDESolver { /// but may be useful for subclasses of {@link IDESolver}) /// void propagate(d_t SourceVal, n_t Target, d_t TargetVal, - const EdgeFunction &f, - /* deliberately exposed to clients */ - n_t /*RelatedCallSite*/, - /* deliberately exposed to clients */ - bool /*IsUnbalancedReturn*/) { + EdgeFunction f) { PHASAR_LOG_LEVEL(DEBUG, "Propagate flow"); - PHASAR_LOG_LEVEL(DEBUG, - "Source value : " << IDEProblem.DtoString(SourceVal)); - PHASAR_LOG_LEVEL(DEBUG, "Target : " << IDEProblem.NtoString(Target)); - PHASAR_LOG_LEVEL(DEBUG, - "Target value : " << IDEProblem.DtoString(TargetVal)); + PHASAR_LOG_LEVEL(DEBUG, "Source value : " << DToString(SourceVal)); + PHASAR_LOG_LEVEL(DEBUG, "Target : " << NToString(Target)); + PHASAR_LOG_LEVEL(DEBUG, "Target value : " << DToString(TargetVal)); PHASAR_LOG_LEVEL( DEBUG, "Edge function : " << f << " (result of previous compose)"); @@ -1220,30 +1156,31 @@ class IDESolver { EdgeFunction fPrime = JumpFnE.joinWith(f); bool NewFunction = fPrime != JumpFnE; - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL( - DEBUG, "Join: " << JumpFnE << " & " << f - << (JumpFnE == f ? " (EF's are equal)" : " ")); - PHASAR_LOG_LEVEL(DEBUG, - " = " << fPrime + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL(DEBUG, + "Join: " << JumpFnE << " & " << f + << (JumpFnE == f ? " (EF's are equal)" : " ")); + PHASAR_LOG_LEVEL(DEBUG, " = " + << fPrime << (NewFunction ? " (new jump func)" : " ")); - PHASAR_LOG_LEVEL(DEBUG, ' ')); + PHASAR_LOG_LEVEL(DEBUG, ' '); + }); if (NewFunction) { JumpFn->addFunction(SourceVal, Target, TargetVal, fPrime); - const PathEdge Edge(SourceVal, Target, TargetVal); + PathEdge Edge(SourceVal, Target, TargetVal); PathEdgeCount++; - pathEdgeProcessingTask(Edge); - - IF_LOG_ENABLED(if (!IDEProblem.isZeroValue(TargetVal)) { - PHASAR_LOG_LEVEL( - DEBUG, "EDGE: getFunction()->getName() << ", D: " - << IDEProblem.DtoString(SourceVal) << '>'); - PHASAR_LOG_LEVEL(DEBUG, - " ---> '); - PHASAR_LOG_LEVEL(DEBUG, ' '); + pathEdgeProcessingTask(std::move(Edge)); + + IF_LOG_LEVEL_ENABLED(DEBUG, { + if (!IDEProblem.isZeroValue(TargetVal)) { + PHASAR_LOG_LEVEL( + DEBUG, "EDGE: getFunction()->getName() + << ", D: " << DToString(SourceVal) << '>'); + PHASAR_LOG_LEVEL(DEBUG, " ---> '); + PHASAR_LOG_LEVEL(DEBUG, ' '); + } }); } else { PHASAR_LOG_LEVEL(DEBUG, "PROPAGATE: No new function!"); @@ -1277,47 +1214,43 @@ class IDESolver { } void printIncomingTab() const { -#ifdef DYNAMIC_LOG - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL(DEBUG, "Start of incomingtab entry"); - for (const auto &Cell - : IncomingTab.cellSet()) { - PHASAR_LOG_LEVEL(DEBUG, - "sP: " << IDEProblem.NtoString(Cell.getRowKey())); - PHASAR_LOG_LEVEL(DEBUG, - "d3: " << IDEProblem.DtoString(Cell.getColumnKey())); - for (const auto &Entry : Cell.getValue()) { - PHASAR_LOG_LEVEL(DEBUG, - " n: " << IDEProblem.NtoString(Entry.first)); - for (const auto &Fact : Entry.second) { - PHASAR_LOG_LEVEL(DEBUG, " d2: " << IDEProblem.DtoString(Fact)); - } + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL(DEBUG, "Start of incomingtab entry"); + for (const auto &Cell : IncomingTab.cellSet()) { + PHASAR_LOG_LEVEL(DEBUG, "sP: " << NToString(Cell.getRowKey())); + PHASAR_LOG_LEVEL(DEBUG, "d3: " << DToString(Cell.getColumnKey())); + for (const auto &Entry : Cell.getValue()) { + PHASAR_LOG_LEVEL(DEBUG, " n: " << NToString(Entry.first)); + for (const auto &Fact : Entry.second) { + PHASAR_LOG_LEVEL(DEBUG, " d2: " << DToString(Fact)); } - PHASAR_LOG_LEVEL(DEBUG, "---------------"); - } PHASAR_LOG_LEVEL(DEBUG, "End of incomingtab entry");) -#endif + } + PHASAR_LOG_LEVEL(DEBUG, "---------------"); + } + PHASAR_LOG_LEVEL(DEBUG, "End of incomingtab entry"); + }) } void printEndSummaryTab() const { -#ifdef DYNAMIC_LOG - IF_LOG_ENABLED( - PHASAR_LOG_LEVEL(DEBUG, "Start of endsummarytab entry"); - for (const auto &Cell - : EndsummaryTab.cellVec()) { - PHASAR_LOG_LEVEL(DEBUG, - "sP: " << IDEProblem.NtoString(Cell.getRowKey())); - PHASAR_LOG_LEVEL(DEBUG, - "d1: " << IDEProblem.DtoString(Cell.getColumnKey())); - for (const auto &InnerCell : Cell.getValue().cellVec()) { - PHASAR_LOG_LEVEL( - DEBUG, " eP: " << IDEProblem.NtoString(InnerCell.getRowKey())); - PHASAR_LOG_LEVEL(DEBUG, " d2: " << IDEProblem.DtoString( - InnerCell.getColumnKey())); - PHASAR_LOG_LEVEL(DEBUG, " EF: " << InnerCell.getValue()); - } - PHASAR_LOG_LEVEL(DEBUG, "---------------"); - } PHASAR_LOG_LEVEL(DEBUG, "End of endsummarytab entry");) -#endif + IF_LOG_LEVEL_ENABLED(DEBUG, { + PHASAR_LOG_LEVEL(DEBUG, "Start of endsummarytab entry"); + + EndsummaryTab.foreachCell( + [](const auto &Row, const auto &Col, const auto &Val) { + PHASAR_LOG_LEVEL(DEBUG, "sP: " << NToString(Row)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(Col)); + + Val.foreachCell([](const auto &InnerRow, const auto &InnerCol, + const auto &InnerVal) { + PHASAR_LOG_LEVEL(DEBUG, " eP: " << NToString(InnerRow)); + PHASAR_LOG_LEVEL(DEBUG, " d2: " << DToString(InnerCol)); + PHASAR_LOG_LEVEL(DEBUG, " EF: " << InnerVal); + }); + PHASAR_LOG_LEVEL(DEBUG, "---------------"); + }); + + PHASAR_LOG_LEVEL(DEBUG, "End of endsummarytab entry"); + }) } void printComputedPathEdges() { @@ -1336,15 +1269,15 @@ class IDESolver { }); for (const auto &Cell : Cells) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - std::string N2Label = IDEProblem.NtoString(Edge.second); - llvm::outs() << "\nN1: " << IDEProblem.NtoString(Edge.first) << '\n' + std::string N2Label = NToString(Edge.second); + llvm::outs() << "\nN1: " << NToString(Edge.first) << '\n' << "N2: " << N2Label << "\n----" << std::string(N2Label.size(), '-') << '\n'; for (auto D1ToD2Set : Cell.getValue()) { auto D1Fact = D1ToD2Set.first; - llvm::outs() << "D1: " << IDEProblem.DtoString(D1Fact) << '\n'; + llvm::outs() << "D1: " << DToString(D1Fact) << '\n'; for (auto D2Fact : D1ToD2Set.second) { - llvm::outs() << "\tD2: " << IDEProblem.DtoString(D2Fact) << '\n'; + llvm::outs() << "\tD2: " << DToString(D2Fact) << '\n'; } llvm::outs() << '\n'; } @@ -1364,15 +1297,15 @@ class IDESolver { }); for (const auto &Cell : Cells) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - std::string N2Label = IDEProblem.NtoString(Edge.second); - llvm::outs() << "\nN1: " << IDEProblem.NtoString(Edge.first) << '\n' + std::string N2Label = NToString(Edge.second); + llvm::outs() << "\nN1: " << NToString(Edge.first) << '\n' << "N2: " << N2Label << "\n----" << std::string(N2Label.size(), '-') << '\n'; for (auto D1ToD2Set : Cell.getValue()) { auto D1Fact = D1ToD2Set.first; - llvm::outs() << "D1: " << IDEProblem.DtoString(D1Fact) << '\n'; + llvm::outs() << "D1: " << DToString(D1Fact) << '\n'; for (auto D2Fact : D1ToD2Set.second) { - llvm::outs() << "\tD2: " << IDEProblem.DtoString(D2Fact) << '\n'; + llvm::outs() << "\tD2: " << DToString(D2Fact) << '\n'; } llvm::outs() << '\n'; } @@ -1399,7 +1332,6 @@ class IDESolver { // key std::unordered_map> ValidInCallerContext; size_t NumGenFacts = 0; - size_t NumKillFacts = 0; size_t NumIntraPathEdges = 0; size_t NumInterPathEdges = 0; // --- Intra-procedural Path Edges --- @@ -1408,10 +1340,10 @@ class IDESolver { // Case 2: d1 not in d2-Set, i.e., d1 was killed. d2-Set could be empty. for (const auto &Cell : ComputedIntraPathEdges.cellSet()) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - PHASAR_LOG_LEVEL(DEBUG, "N1: " << IDEProblem.NtoString(Edge.first)); - PHASAR_LOG_LEVEL(DEBUG, "N2: " << IDEProblem.NtoString(Edge.second)); + PHASAR_LOG_LEVEL(DEBUG, "N1: " << NToString(Edge.first)); + PHASAR_LOG_LEVEL(DEBUG, "N2: " << NToString(Edge.second)); for (auto &[D1, D2s] : Cell.getValue()) { - PHASAR_LOG_LEVEL(DEBUG, "d1: " << IDEProblem.DtoString(D1)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(D1)); NumIntraPathEdges += D2s.size(); // Case 1 if (D2s.find(D1) != D2s.end()) { @@ -1420,18 +1352,14 @@ class IDESolver { // Case 2 else { NumGenFacts += D2s.size(); - // We ignore the zero value - if (!IDEProblem.isZeroValue(D1)) { - NumKillFacts++; - } } // Store all valid facts after call-to-return flow if (ICF->isCallSite(Edge.first)) { ValidInCallerContext[Edge.second].insert(D2s.begin(), D2s.end()); } - IF_LOG_ENABLED([this](const auto &D2s) { + IF_LOG_LEVEL_ENABLED(DEBUG, [this](const auto &D2s) { for (auto D2 : D2s) { - PHASAR_LOG_LEVEL(DEBUG, "d2: " << IDEProblem.DtoString(D2)); + PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2)); } PHASAR_LOG_LEVEL(DEBUG, "----"); }(D2s)); @@ -1444,8 +1372,8 @@ class IDESolver { PHASAR_LOG_LEVEL(DEBUG, "INTER PATH EDGES"); for (const auto &Cell : ComputedInterPathEdges.cellSet()) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - PHASAR_LOG_LEVEL(DEBUG, "N1: " << IDEProblem.NtoString(Edge.first)); - PHASAR_LOG_LEVEL(DEBUG, "N2: " << IDEProblem.NtoString(Edge.second)); + PHASAR_LOG_LEVEL(DEBUG, "N1: " << NToString(Edge.first)); + PHASAR_LOG_LEVEL(DEBUG, "N2: " << NToString(Edge.second)); // --- Call-flow Path Edges --- // Case 1: d1 --> empty set // Can be ignored, since killing a fact in the caller context will @@ -1462,7 +1390,7 @@ class IDESolver { // Process the summary's #gen and #kill. if (ICF->isCallSite(Edge.first)) { for (auto &[D1, D2s] : Cell.getValue()) { - PHASAR_LOG_LEVEL(DEBUG, "d1: " << IDEProblem.DtoString(D1)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(D1)); NumInterPathEdges += D2s.size(); for (auto D2 : D2s) { if (!IDEProblem.isZeroValue(D2)) { @@ -1471,25 +1399,24 @@ class IDESolver { // Special case if (ProcessSummaryFacts.find(std::make_pair(Edge.second, D2)) != ProcessSummaryFacts.end()) { - std::multiset SummaryDMultiSet = - EndsummaryTab.get(Edge.second, D2).columnKeySet(); - // remove duplicates from multiset - std::set SummaryDSet(SummaryDMultiSet.begin(), - SummaryDMultiSet.end()); + + std::set SummaryDSet; + EndsummaryTab.get(Edge.second, D2) + .foreachCell([&SummaryDSet](const auto &Row, const auto &Col, + const auto &Val) { + SummaryDSet.insert(Col); + }); + // Process summary just as an intra-procedural edge if (SummaryDSet.find(D2) != SummaryDSet.end()) { NumGenFacts += SummaryDSet.size() - 1; } else { NumGenFacts += SummaryDSet.size(); - // We ignore the zero value - if (!IDEProblem.isZeroValue(D1)) { - NumKillFacts++; - } } } else { ProcessSummaryFacts.emplace(Edge.second, D2); } - PHASAR_LOG_LEVEL(DEBUG, "d2: " << IDEProblem.DtoString(D2)); + PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2)); } PHASAR_LOG_LEVEL(DEBUG, "----"); } @@ -1503,7 +1430,7 @@ class IDESolver { // Zero value does not count towards generated/killed facts. if (ICF->isExitInst(Cell.getRowKey())) { for (auto &[D1, D2s] : Cell.getValue()) { - PHASAR_LOG_LEVEL(DEBUG, "d1: " << IDEProblem.DtoString(D1)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(D1)); NumInterPathEdges += D2s.size(); auto CallerFacts = ValidInCallerContext[Edge.second]; for (auto D2 : D2s) { @@ -1511,10 +1438,7 @@ class IDESolver { if (CallerFacts.find(D2) == CallerFacts.end()) { NumGenFacts++; } - PHASAR_LOG_LEVEL(DEBUG, "d2: " << IDEProblem.DtoString(D2)); - } - if (!IDEProblem.isZeroValue(D1)) { - NumKillFacts++; + PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2)); } PHASAR_LOG_LEVEL(DEBUG, "----"); } @@ -1524,20 +1448,15 @@ class IDESolver { PHASAR_LOG_LEVEL(DEBUG, "SUMMARY REUSE"); std::size_t TotalSummaryReuse = 0; for (const auto &Entry : FSummaryReuse) { - PHASAR_LOG_LEVEL(DEBUG, - "N1: " << IDEProblem.NtoString(Entry.first.first)); - PHASAR_LOG_LEVEL(DEBUG, - "D1: " << IDEProblem.DtoString(Entry.first.second)); + PHASAR_LOG_LEVEL(DEBUG, "N1: " << NToString(Entry.first.first)); + PHASAR_LOG_LEVEL(DEBUG, "D1: " << DToString(Entry.first.second)); PHASAR_LOG_LEVEL(DEBUG, "#Reuse: " << Entry.second); TotalSummaryReuse += Entry.second; } - INC_COUNTER("Gen facts", NumGenFacts, PAMM_SEVERITY_LEVEL::Core); - INC_COUNTER("Kill facts", NumKillFacts, PAMM_SEVERITY_LEVEL::Core); - INC_COUNTER("Summary-reuse", TotalSummaryReuse, PAMM_SEVERITY_LEVEL::Core); - INC_COUNTER("Intra Path Edges", NumIntraPathEdges, - PAMM_SEVERITY_LEVEL::Core); - INC_COUNTER("Inter Path Edges", NumInterPathEdges, - PAMM_SEVERITY_LEVEL::Core); + INC_COUNTER("Gen facts", NumGenFacts, Core); + INC_COUNTER("Summary-reuse", TotalSummaryReuse, Core); + INC_COUNTER("Intra Path Edges", NumIntraPathEdges, Core); + INC_COUNTER("Inter Path Edges", NumInterPathEdges, Core); PHASAR_LOG_LEVEL(INFO, "----------------------------------------------"); PHASAR_LOG_LEVEL(INFO, "=== Solver Statistics ==="); @@ -1593,8 +1512,8 @@ class IDESolver { }); for (const auto &Cell : Cells) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - std::string N1Label = IDEProblem.NtoString(Edge.first); - std::string N2Label = IDEProblem.NtoString(Edge.second); + std::string N1Label = NToString(Edge.first); + std::string N2Label = NToString(Edge.second); PHASAR_LOG_LEVEL(DEBUG, "N1: " << N1Label); PHASAR_LOG_LEVEL(DEBUG, "N2: " << N2Label); std::string N1StmtId = ICF->getStatementId(Edge.first); @@ -1623,7 +1542,7 @@ class IDESolver { unsigned D2FactId = 0; for (const auto &D1ToD2Set : Cell.getValue()) { auto D1Fact = D1ToD2Set.first; - PHASAR_LOG_LEVEL(DEBUG, "d1: " << IDEProblem.DtoString(D1Fact)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(D1Fact)); DOTNode D1; if (IDEProblem.isZeroValue(D1Fact)) { @@ -1632,7 +1551,7 @@ class IDESolver { } else { // Get the fact-ID D1FactId = G.getFactID(D1Fact); - std::string D1Label = IDEProblem.DtoString(D1Fact); + std::string D1Label = DToString(D1Fact); // Get or create the fact subgraph D1FSG = FG->getOrCreateFactSG(D1FactId, D1Label); @@ -1644,13 +1563,13 @@ class IDESolver { DOTFactSubGraph *D2FSG = nullptr; for (const auto &D2Fact : D1ToD2Set.second) { - PHASAR_LOG_LEVEL(DEBUG, "d2: " << IDEProblem.DtoString(D2Fact)); + PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2Fact)); // We do not need to generate any intra-procedural nodes and edges // for the zero value since they will be auto-generated if (!IDEProblem.isZeroValue(D2Fact)) { // Get the fact-ID D2FactId = G.getFactID(D2Fact); - std::string D2Label = IDEProblem.DtoString(D2Fact); + std::string D2Label = DToString(D2Fact); DOTNode D2 = {FuncName, D2Label, N2StmtId, D2FactId, false, true}; std::string EFLabel; auto EFVec = IntermediateEdgeFunctions[std::make_tuple( @@ -1686,8 +1605,8 @@ class IDESolver { }); for (const auto &Cell : Cells) { auto Edge = std::make_pair(Cell.getRowKey(), Cell.getColumnKey()); - std::string N1Label = IDEProblem.NtoString(Edge.first); - std::string N2Label = IDEProblem.NtoString(Edge.second); + std::string N1Label = NToString(Edge.first); + std::string N2Label = NToString(Edge.second); std::string FNameOfN1 = ICF->getFunctionOf(Edge.first)->getName().str(); std::string FNameOfN2 = ICF->getFunctionOf(Edge.second)->getName().str(); std::string N1StmtId = ICF->getStatementId(Edge.first); @@ -1728,14 +1647,14 @@ class IDESolver { unsigned D2FactId = 0; for (const auto &D1ToD2Set : Cell.getValue()) { auto D1Fact = D1ToD2Set.first; - PHASAR_LOG_LEVEL(DEBUG, "d1: " << IDEProblem.DtoString(D1Fact)); + PHASAR_LOG_LEVEL(DEBUG, "d1: " << DToString(D1Fact)); DOTNode D1; if (IDEProblem.isZeroValue(D1Fact)) { D1 = {FNameOfN1, "Λ", N1StmtId, 0, false, true}; } else { // Get the fact-ID D1FactId = G.getFactID(D1Fact); - std::string D1Label = IDEProblem.DtoString(D1Fact); + std::string D1Label = DToString(D1Fact); D1 = {FNameOfN1, D1Label, N1StmtId, D1FactId, false, true}; // FG should already exist even for single statement functions if (!G.containsFactSG(FNameOfN1, D1FactId)) { @@ -1747,14 +1666,14 @@ class IDESolver { auto D2Set = D1ToD2Set.second; for (const auto &D2Fact : D2Set) { - PHASAR_LOG_LEVEL(DEBUG, "d2: " << IDEProblem.DtoString(D2Fact)); + PHASAR_LOG_LEVEL(DEBUG, "d2: " << DToString(D2Fact)); DOTNode D2; if (IDEProblem.isZeroValue(D2Fact)) { D2 = {FNameOfN2, "Λ", N2StmtId, 0, false, true}; } else { // Get the fact-ID D2FactId = G.getFactID(D2Fact); - std::string D2Label = IDEProblem.DtoString(D2Fact); + std::string D2Label = DToString(D2Fact); D2 = {FNameOfN2, D2Label, N2StmtId, D2FactId, false, true}; // FG should already exist even for single statement functions if (!G.containsFactSG(FNameOfN2, D2FactId)) { @@ -1790,6 +1709,7 @@ class IDESolver { OS << G; } +private: /// @brief: Allows less-than comparison based on the statement ID. struct StmtLess { const i_t *ICF; @@ -1799,6 +1719,128 @@ class IDESolver { return StrIDLess(ICF->getStatementId(Lhs), ICF->getStatementId(Rhs)); } }; + + /// -- InteractiveIDESolverMixin implementation + + bool doInitialize() { + PAMM_GET_INSTANCE; + REG_COUNTER("Gen facts", 0, Core); + REG_COUNTER("Kill facts", 0, Core); + REG_COUNTER("Summary-reuse", 0, Core); + REG_COUNTER("Intra Path Edges", 0, Core); + REG_COUNTER("Inter Path Edges", 0, Core); + REG_COUNTER("FF Queries", 0, Full); + REG_COUNTER("EF Queries", 0, Full); + REG_COUNTER("Value Propagation", 0, Full); + REG_COUNTER("Value Computation", 0, Full); + REG_COUNTER("SpecialSummary-FF Application", 0, Full); + REG_COUNTER("SpecialSummary-EF Queries", 0, Full); + REG_COUNTER("JumpFn Construction", 0, Full); + REG_COUNTER("Process Call", 0, Full); + REG_COUNTER("Process Normal", 0, Full); + REG_COUNTER("Process Exit", 0, Full); + REG_COUNTER("[Calls] getAliasSet", 0, Full); + REG_HISTOGRAM("Data-flow facts", Full); + REG_HISTOGRAM("Points-to", Full); + + PHASAR_LOG_LEVEL(INFO, "IDE solver is solving the specified problem"); + PHASAR_LOG_LEVEL(INFO, + "Submit initial seeds, construct exploded super graph"); + // computations starting here + START_TIMER("DFA Phase I", Full); + + // We start our analysis and construct exploded supergraph + submitInitialSeeds(); + return !WorkList.empty(); + } + + bool doNext() { + assert(!WorkList.empty()); + auto [Edge, EF] = std::move(WorkList.back()); + WorkList.pop_back(); + + auto [SourceVal, Target, TargetVal] = Edge.consume(); + propagate(std::move(SourceVal), std::move(Target), std::move(TargetVal), + std::move(EF)); + + return !WorkList.empty(); + } + + void finalizeInternal() { + PAMM_GET_INSTANCE; + STOP_TIMER("DFA Phase I", Full); + PHASAR_LOG_LEVEL(INFO, "[info]: IDE Phase I completed"); + + if (SolverConfig.computeValues()) { + START_TIMER("DFA Phase II", Full); + // Computing the final values for the edge functions + PHASAR_LOG_LEVEL( + INFO, "Compute the final values according to the edge functions"); + computeValues(); + STOP_TIMER("DFA Phase II", Full); + } + + PHASAR_LOG_LEVEL(INFO, "Problem solved"); + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::Core) { + computeAndPrintStatistics(); + } + if (SolverConfig.emitESG()) { + emitESGAsDot(); + } + } + + SolverResults doFinalize() & { + finalizeInternal(); + return getSolverResults(); + } + + OwningSolverResults doFinalize() && { + finalizeInternal(); + return consumeSolverResults(); + } + + /// -- Data members + + IDETabulationProblem &IDEProblem; + d_t ZeroValue; + const i_t *ICF; + IFDSIDESolverConfig &SolverConfig; + + std::vector, EdgeFunction>> WorkList; + std::vector> ValuePropWL; + + size_t PathEdgeCount = 0; + + FlowEdgeFunctionCache CachedFlowEdgeFunctions; + + Table> ComputedIntraPathEdges; + + Table> ComputedInterPathEdges; + + EdgeFunction AllTop; + + std::shared_ptr> JumpFn; + + std::map, std::vector>> + IntermediateEdgeFunctions; + + // stores summaries that were queried before they were computed + // see CC 2010 paper by Naeem, Lhotak and Rodriguez + Table>> EndsummaryTab; + + // edges going along calls + // see CC 2010 paper by Naeem, Lhotak and Rodriguez + Table> IncomingTab; + + // stores the return sites (inside callers) to which we have unbalanced + // returns if SolverConfig.followReturnPastSeeds is enabled + std::set UnbalancedRetSites; + + InitialSeeds Seeds; + + Table ValTab; + + std::map, size_t> FSummaryReuse; }; template @@ -1818,6 +1860,17 @@ template using IDESolver_P = IDESolver; +template +OwningSolverResults +solveIDEProblem(IDETabulationProblem &Problem, + const typename AnalysisDomainTy::i_t &ICF) { + IDESolver Solver(Problem, &ICF); + Solver.solve(); + return Solver.consumeSolverResults(); +} + } // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolverAPIMixin.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolverAPIMixin.h new file mode 100644 index 000000000..65ae8a890 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolverAPIMixin.h @@ -0,0 +1,536 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DATAFLOW_IFDSIDE_SOLVER_IDESOLVERAPIMIXIN_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVERAPIMIXIN_H + +#include "phasar/Utils/Logger.h" + +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace psr { +template class IDESolverAPIMixin { +public: + /// Initialize the IDE solver for step-wise solving (iteratively calling + /// next() or nextN()). + /// For a more high-level API use solveUntil() or solveTimeout(). + /// + /// \returns True, iff it is valid to call next() or nextN() afterwards. + [[nodiscard]] bool initialize() { return self().doInitialize(); } + + /// Performs one tiny step towards the analysis' fixpoint. For a + /// more high-level API use solveUntil() or solveTimeout(). + /// + /// Requires that initialize() has been called before once and returned true + /// and that all previous next() or nextN() calls returned true as well. + /// + /// \returns True, iff there are more steps to process before calling + /// finalize() + [[nodiscard]] bool next() { return self().doNext(); } + + /// Performs N tiny steps towards the analysis' fixpoint. + /// For a more high-level API use solveUntil() or solveTimeout(). + /// + /// Requires that initialize() has been called before once and returned true + /// and that all previous next() or nextN() calls returned true as well. + /// + /// \returns True, iff there are more steps to process before calling + /// finalize() + [[nodiscard]] bool nextN(size_t MaxNumIterations) { + PHASAR_LOG_LEVEL(DEBUG, + "[nextN]: Next " << MaxNumIterations << " Iterations"); + + for (size_t I = 0; I != MaxNumIterations; ++I) { + if (!next()) { + PHASAR_LOG_LEVEL(DEBUG, "[nextN]: > done after " << I << " iterations"); + return false; + } + } + PHASAR_LOG_LEVEL(DEBUG, "[nextN]: > has next"); + return true; + } + + /// Computes the final analysis results after the analysis has reached its + /// fixpoint, i.e. either initialize() returned false or the last next() or + /// nextN() call returned false. + /// + /// \returns A view into the computed analysis results + decltype(auto) finalize() & { return self().doFinalize(); } + /// Computes the final analysis results after the analysis has reached its + /// fixpoint, i.e. either initialize() returned false or the last next() or + /// nextN() call returned false. + /// + /// \returns The computed analysis results + decltype(auto) finalize() && { return std::move(self()).doFinalize(); } + + /// Runs the solver on the configured problem. This can take some time and + /// cannot be interrupted. If you need the ability to interrupt the solving + /// process consider using solveUntil() or solveTimeout(). + /// + /// \returns A view into the computed analysis results + decltype(auto) solve() & { + solveImpl(); + return finalize(); + } + + /// Runs the solver on the configured problem. This can take some time and + /// cannot be interrupted. If you need the ability to interrupt the solving + /// process consider using solveUntil() or solveTimeout(). + /// + /// \returns The computed analysis results + decltype(auto) solve() && { + solveImpl(); + return std::move(*this).finalize(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. This can take some time and cannot be + /// interrupted. If you need the ability to interrupt the solving process + /// consider using solveUntil() or solveTimeout(). + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// \returns A view into the computed analysis results + decltype(auto) continueSolving() & { + continueImpl(); + return finalize(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. This can take some time and cannot be + /// interrupted. If you need the ability to interrupt the solving process + /// consider using solveUntil() or solveTimeout(). + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// \returns The computed analysis results + decltype(auto) continueSolving() && { + continueImpl(); + return std::move(*this).finalize(); + } + + /// Solves the analysis problem and periodically checks every Interval whether + /// CancellationRequested evaluates to true. + /// + /// Note: Shortening the cancellation-check interval will make the + /// cancellation-time more precise (by having more frequent calls to + /// CancellationRequested) but also have negative impact on the solver's + /// performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + template || + std::is_invocable_r_v>> + auto + solveUntil(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval = std::chrono::seconds{1}) & { + using RetTy = std::optional>; + return [&]() -> RetTy { + if (solveUntilImpl(std::move(CancellationRequested), Interval)) { + return finalize(); + } + return std::nullopt; + }(); + } + + /// Solves the analysis problem and periodically checks every Interval whether + /// CancellationRequested evaluates to true. + /// + /// Note: Shortening the cancellation-check interval will make the + /// cancellation-time more precise (by having more frequent calls to + /// CancellationRequested) but also have negative impact on the solver's + /// performance. + /// + /// \returns An std::optional holding the analysis results or std::nullopt if + /// the analysis was cancelled. + template || + std::is_invocable_r_v>> + auto + solveUntil(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval = std::chrono::seconds{1}) && { + using RetTy = + std::optional>; + return [&]() -> RetTy { + if (solveUntilImpl(std::move(CancellationRequested), Interval)) { + return std::move(*this).finalize(); + } + return std::nullopt; + }(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks every Interval whether + /// CancellationRequested evaluates to true. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// Note: Shortening the cancellation-check interval will make the + /// cancellation-time more precise (by having more frequent calls to + /// CancellationRequested) but also have negative impact on the solver's + /// performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + template || + std::is_invocable_r_v>> + auto continueUntil(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval = std::chrono::seconds{ + 1}) & { + using RetTy = std::optional>; + return [&]() -> RetTy { + if (continueUntilImpl(std::move(CancellationRequested), Interval)) { + return finalize(); + } + return std::nullopt; + }(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks every Interval whether + /// CancellationRequested evaluates to true. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// Note: Shortening the cancellation-check interval will make the + /// cancellation-time more precise (by having more frequent calls to + /// CancellationRequested) but also have negative impact on the solver's + /// performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + template || + std::is_invocable_r_v>> + auto continueUntil(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval = std::chrono::seconds{ + 1}) && { + using RetTy = + std::optional>; + return [&]() -> RetTy { + if (continueUntilImpl(std::move(CancellationRequested), Interval)) { + return std::move(*this).finalize(); + } + return std::nullopt; + }(); + } + + /// Solves the analysis problem and periodically checks every Interval whether + /// the Timeout has been exceeded. + /// + /// Note: Shortening the timeout-check interval will make the timeout more + /// precise but also have negative impact on the solver's performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto solveWithTimeout(std::chrono::milliseconds Timeout, + std::chrono::milliseconds Interval) & { + auto CancellationRequested = + [Timeout, Start = std::chrono::steady_clock::now()]( + std::chrono::steady_clock::time_point TimeStamp) { + return TimeStamp - Start >= Timeout; + }; + + return solveUntil(CancellationRequested, Interval); + } + + /// Solves the analysis problem and periodically checks every Interval whether + /// the Timeout has been exceeded. + /// + /// Note: Shortening the timeout-check interval will make the timeout more + /// precise but also have negative impact on the solver's performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto solveWithTimeout(std::chrono::milliseconds Timeout, + std::chrono::milliseconds Interval) && { + auto CancellatioNRequested = + [Timeout, Start = std::chrono::steady_clock::now()]( + std::chrono::steady_clock::time_point TimeStamp) { + return TimeStamp - Start >= Timeout; + }; + return std::move(*this).solveUntil(CancellatioNRequested, Interval); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks every Interval whether + /// the Timeout has been exceeded. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// Note: Shortening the timeout-check interval will make the timeout more + /// precise but also have negative impact on the solver's performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto continueWithTimeout(std::chrono::milliseconds Timeout, + std::chrono::milliseconds Interval) & { + auto CancellationRequested = + [Timeout, Start = std::chrono::steady_clock::now()]( + std::chrono::steady_clock::time_point TimeStamp) { + return TimeStamp - Start >= Timeout; + }; + + return continueUntil(CancellationRequested, Interval); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks every Interval whether + /// the Timeout has been exceeded. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// Note: Shortening the timeout-check interval will make the timeout more + /// precise but also have negative impact on the solver's performance. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto continueWithTimeout(std::chrono::milliseconds Timeout, + std::chrono::milliseconds Interval) && { + auto CancellationRequested = + [Timeout, Start = std::chrono::steady_clock::now()]( + std::chrono::steady_clock::time_point TimeStamp) { + return TimeStamp - Start >= Timeout; + }; + + return std::move(*this).continueUntil(CancellationRequested, Interval); + } + + // -- Async cancellation + + /// Solves the analysis problem and periodically checks whether + /// IsCancelled is true. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto solveWithAsyncCancellation(std::atomic_bool &IsCancelled) & { + using RetTy = std::optional>; + return [&]() -> RetTy { + if (solveWithAsyncCancellationImpl(IsCancelled)) { + return finalize(); + } + return std::nullopt; + }(); + } + + /// Solves the analysis problem and periodically checks whether + /// IsCancelled is true. + /// + /// \returns An std::optional holding the analysis results or std::nullopt if + /// the analysis was cancelled. + auto solveWithAsyncCancellation(std::atomic_bool &IsCancelled) && { + using RetTy = + std::optional>; + return [&]() -> RetTy { + if (solveWithAsyncCancellationImpl(IsCancelled)) { + return std::move(*this).finalize(); + } + return std::nullopt; + }(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks whether + /// IsCancelled is true. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto continueWithAsyncCancellation(std::atomic_bool &IsCancelled) & { + using RetTy = std::optional>; + return [&]() -> RetTy { + if (continueWithAsyncCancellationImpl(IsCancelled)) { + return finalize(); + } + return std::nullopt; + }(); + } + + /// Continues running the solver on the configured problem after it got + /// interrupted in a previous run. Periodically checks whether + /// IsCancelled is true. + /// + /// \remark Please make sure to *only* call this function on an IDESolver + /// where the solving process is interrupted, i.e. one of the interruptable + /// solving methods returned std::nullopt. It is *invalid* to call this + /// function on an IDESolver that has not yet started solving or has already + /// flinalized solving. + /// + /// \returns An std::optional holding a view into the analysis results or + /// std::nullopt if the analysis was cancelled. + auto continueWithAsyncCancellation(std::atomic_bool &IsCancelled) && { + using RetTy = + std::optional>; + return [&]() -> RetTy { + if (continueWithAsyncCancellationImpl(IsCancelled)) { + return std::move(*this).finalize(); + } + return std::nullopt; + }(); + } + +private: + [[nodiscard]] Derived &self() &noexcept { + static_assert(std::is_base_of_v, + "Invalid CRTP instantiation"); + return static_cast(*this); + } + + void continueImpl() { + while (next()) { + // no interrupt in normal solving process + } + } + + void solveImpl() { + if (initialize()) { + continueImpl(); + } + } + + template + [[nodiscard]] bool + continueUntilImpl(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval) { + auto IsCancellationRequested = + [&CancellationRequested]( + std::chrono::steady_clock::time_point TimeStamp) { + if constexpr (std::is_invocable_r_v< + bool, CancellationRequest, + std::chrono::steady_clock::time_point>) { + return std::invoke(CancellationRequested, TimeStamp); + } else { + return std::invoke(CancellationRequested); + } + }; + + // Some initial number of propagations to get an idea, how long a + // propagation takes. This may be adjusted in the future + size_t NumIterations = Interval.count() * 500; + + auto Start = std::chrono::steady_clock::now(); + + while (nextN(NumIterations)) { + auto End = std::chrono::steady_clock::now(); + using milliseconds_d = std::chrono::duration; + + auto DeltaTime = std::chrono::duration_cast(End - Start); + Start = End; + + if (IsCancellationRequested(End)) { + return false; + } + + // Adjust NumIterations + auto IterationsPerMilli = double(NumIterations) / DeltaTime.count(); + auto NewNumIterations = + size_t(IterationsPerMilli * double(Interval.count())); + NumIterations = (NumIterations + 2 * NewNumIterations) / 3; + } + auto End = std::chrono::steady_clock::now(); + return !IsCancellationRequested(End); + } + + template + [[nodiscard]] bool solveUntilImpl(CancellationRequest CancellationRequested, + std::chrono::milliseconds Interval) { + auto IsCancellationRequested = + [&CancellationRequested]( + std::chrono::steady_clock::time_point TimeStamp) { + if constexpr (std::is_invocable_r_v< + bool, CancellationRequest, + std::chrono::steady_clock::time_point>) { + return std::invoke(CancellationRequested, TimeStamp); + } else { + return std::invoke(CancellationRequested); + } + }; + + bool Initialized = initialize(); + auto TimeStamp = std::chrono::steady_clock::now(); + if (IsCancellationRequested(TimeStamp)) { + return false; + } + + return Initialized + ? continueUntilImpl(std::move(CancellationRequested), Interval) + : true; + } + + [[nodiscard]] bool + continueWithAsyncCancellationImpl(std::atomic_bool &IsCancelled) { + while (next()) { + if (IsCancelled.load()) { + return false; + } + } + return !IsCancelled.load(); + } + + [[nodiscard]] bool + solveWithAsyncCancellationImpl(std::atomic_bool &IsCancelled) { + bool Initialized = initialize(); + if (IsCancelled.load()) { + return false; + } + + return Initialized ? continueWithAsyncCancellationImpl(IsCancelled) : true; + } +}; +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVERAPIMIXIN_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index f9ed15d76..3a5f20d8f 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -14,8 +14,8 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IFDSSOLVER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IFDSSOLVER_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_IFDSSOLVER_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IFDSSOLVER_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" @@ -28,8 +28,10 @@ namespace psr { -template -class IFDSSolver : public IDESolver> { +template > +class IFDSSolver + : public IDESolver, Container> { public: using ProblemTy = IFDSTabulationProblem; using d_t = typename AnalysisDomainTy::d_t; @@ -39,10 +41,11 @@ class IFDSSolver : public IDESolver> { template >> - IFDSSolver(IFDSTabulationProblem &IFDSProblem, const i_t *ICF) + IFDSSolver(IFDSTabulationProblem &IFDSProblem, + const i_t *ICF) : IDESolver>(IFDSProblem, ICF) {} - virtual ~IFDSSolver() = default; + ~IFDSSolver() override = default; /// Returns the data-flow results at the given statement. [[nodiscard]] virtual std::set ifdsResultsAt(n_t Inst) { @@ -96,10 +99,23 @@ class IFDSSolver : public IDESolver> { template IFDSSolver(Problem &, ICF *) - -> IFDSSolver; + -> IFDSSolver; template -using IFDSSolver_P = IFDSSolver; +using IFDSSolver_P = IFDSSolver; + +template +OwningSolverResults +solveIFDSProblem(IFDSTabulationProblem &Problem, + const typename AnalysisDomainTy::i_t &ICF) { + IFDSSolver Solver(Problem, &ICF); + Solver.solve(); + return Solver.consumeSolverResults(); +} } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h b/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h index dc3dbac3c..85a80fe60 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h @@ -14,10 +14,11 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_JUMPFUNCTIONS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_JUMPFUNCTIONS_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_JUMPFUNCTIONS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_JUMPFUNCTIONS_H -#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Table.h" @@ -43,9 +44,6 @@ template class JumpFunctions { using d_t = typename AnalysisDomainTy::d_t; using n_t = typename AnalysisDomainTy::n_t; -private: - const IDETabulationProblem &Problem; - protected: // mapping from target node and value to a list of all source values and // associated functions where the list is implemented as a mapping from @@ -64,10 +62,7 @@ template class JumpFunctions { NonEmptyLookupByTargetNode; public: - JumpFunctions( - const IDETabulationProblem &Problem) - : Problem(Problem) {} - + JumpFunctions() noexcept = default; ~JumpFunctions() = default; JumpFunctions(const JumpFunctions &JFs) = default; @@ -82,11 +77,9 @@ template class JumpFunctions { void addFunction(d_t SourceVal, n_t Target, d_t TargetVal, EdgeFunction EdgeFunc) { PHASAR_LOG_LEVEL(DEBUG, "Start adding new jump function"); - PHASAR_LOG_LEVEL(DEBUG, - "Fact at source : " << Problem.DtoString(SourceVal)); - PHASAR_LOG_LEVEL(DEBUG, - "Fact at target : " << Problem.DtoString(TargetVal)); - PHASAR_LOG_LEVEL(DEBUG, "Destination : " << Problem.NtoString(Target)); + PHASAR_LOG_LEVEL(DEBUG, "Fact at source : " << DToString(SourceVal)); + PHASAR_LOG_LEVEL(DEBUG, "Fact at target : " << DToString(TargetVal)); + PHASAR_LOG_LEVEL(DEBUG, "Destination : " << NToString(Target)); PHASAR_LOG_LEVEL(DEBUG, "Edge Function : " << EdgeFunc); // we do not store the default function (all-top) if (llvm::isa>(EdgeFunc)) { @@ -165,6 +158,18 @@ template class JumpFunctions { return NonEmptyLookupByTargetNode[Target]; } + template + void foreachEdgeFunction(HandlerFn Handler) const { + NonEmptyForwardLookup.foreachCell( + [Handler = std::move(Handler)](ByConstRef /*Row*/, + ByConstRef /*Col*/, + const auto &TargetFactAndEF) { + for (const auto &[TargetFact, EF] : TargetFactAndEF) { + std::invoke(Handler, EF); + } + }); + } + /** * Removes a jump function. The source statement is implicit. * @see PathEdge @@ -207,12 +212,12 @@ template class JumpFunctions { OS << "\n* Print all Jump Functions *"; OS << "\n******************************************************\n"; for (auto &Entry : NonEmptyLookupByTargetNode) { - std::string NLabel = Problem.NtoString(Entry.first); + std::string NLabel = NToString(Entry.first); OS << "\nN: " << NLabel << "\n---" << std::string(NLabel.size(), '-') << '\n'; for (auto Cell : Entry.second.cellSet()) { - OS << "D1: " << Problem.DtoString(Cell.r) << '\n' - << "\tD2: " << Problem.DtoString(Cell.c) << '\n' + OS << "D1: " << DToString(Cell.r) << '\n' + << "\tD2: " << DToString(Cell.c) << '\n' << "\tEF: " << Cell.v << "\n\n"; } } @@ -223,11 +228,11 @@ template class JumpFunctions { "EdgeFunctionPtrType>>\n"; auto CellVec = NonEmptyReverseLookup.cellVec(); for (auto Cell : CellVec) { - OS << "N : " << Problem.NtoString(Cell.r) - << "\nD1: " << Problem.DtoString(Cell.c) << '\n'; + OS << "N : " << NToString(Cell.r) << "\nD1: " << DToString(Cell.c) + << '\n'; for (auto D2ToEF : Cell.v) { - OS << "D2: " << Problem.DtoString(D2ToEF.first) - << "\nEF: " << D2ToEF.second << '\n'; + OS << "D2: " << DToString(D2ToEF.first) << "\nEF: " << D2ToEF.second + << '\n'; } OS << '\n'; } @@ -238,11 +243,11 @@ template class JumpFunctions { "EdgeFunctionPtrType>>\n"; auto CellVec = NonEmptyForwardLookup.cellVec(); for (auto Cell : CellVec) { - OS << "D1: " << Problem.DtoString(Cell.r) - << "\nN : " << Problem.NtoString(Cell.c) << '\n'; + OS << "D1: " << DToString(Cell.r) << "\nN : " << NToString(Cell.c) + << '\n'; for (auto D2ToEF : Cell.v) { - OS << "D2: " << Problem.DtoString(D2ToEF.first) - << "\nEF: " << D2ToEF.second << '\n'; + OS << "D2: " << DToString(D2ToEF.first) << "\nEF: " << D2ToEF.second + << '\n'; } OS << '\n'; } @@ -252,13 +257,12 @@ template class JumpFunctions { OS << "DUMP nonEmptyLookupByTargetNode\nstd::unordered_map>\n"; for (auto Node : NonEmptyLookupByTargetNode) { - OS << "\nN : " << Problem.NtoString(Node.first) << '\n'; + OS << "\nN : " << NToString(Node.first) << '\n'; auto Table = NonEmptyLookupByTargetNode[Node.first]; auto CellVec = Table.cellVec(); for (auto Cell : CellVec) { - OS << "D1: " << Problem.DtoString(Cell.r) - << "\nD2: " << Problem.DtoString(Cell.c) << "\nEF: " << Cell.v - << '\n'; + OS << "D1: " << DToString(Cell.r) << "\nD2: " << DToString(Cell.c) + << "\nEF: " << Cell.v << '\n'; } OS << '\n'; } diff --git a/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h new file mode 100644 index 000000000..711932c87 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_IFDSIDE_SOLVER_PATHAWAREIDESOLVER_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_PATHAWAREIDESOLVER_H + +#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h" +#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" +#include "phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h" +#include "phasar/Utils/Logger.h" + +namespace psr { +template > +class PathAwareIDESolver : public IDESolver { + using base_t = IDESolver; + +public: + using domain_t = AnalysisDomainTy; + using n_t = typename base_t::n_t; + using d_t = typename base_t::d_t; + using i_t = typename base_t::i_t; + using container_type = typename base_t::container_type; + + explicit PathAwareIDESolver( + IDETabulationProblem &Problem, const i_t *ICF) + : base_t(Problem, ICF), ESG(Problem.getZeroValue()) { + + if (Problem.getIFDSIDESolverConfig().autoAddZero()) { + PHASAR_LOG_LEVEL( + WARNING, + "The PathAwareIDESolver is initialized with the option 'autoAddZero' " + "being set. This might degrade the quality of the computed paths!"); + } + } + + [[nodiscard]] const ExplodedSuperGraph & + getExplicitESG() const &noexcept { + return ESG; + } + + [[nodiscard]] ExplodedSuperGraph &&getExplicitESG() &&noexcept { + return std::move(ESG); + } + +private: + void saveEdges(n_t Curr, n_t Succ, d_t CurrNode, + const container_type &SuccNodes, ESGEdgeKind Kind) override { + ESG.saveEdges(std::move(Curr), std::move(CurrNode), std::move(Succ), + SuccNodes, Kind); + } + + ExplodedSuperGraph ESG; +}; + +template +PathAwareIDESolver(ProblemTy &) + -> PathAwareIDESolver; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_PATHAWAREIDESOLVER_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/PathEdge.h b/include/phasar/DataFlow/IfdsIde/Solver/PathEdge.h index d5bfffb60..9bbd90c19 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/PathEdge.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/PathEdge.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_PATHEDGE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_PATHEDGE_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_PATHEDGE_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_PATHEDGE_H #include "phasar/Utils/ByRef.h" @@ -24,15 +24,24 @@ template class PathEdge { PathEdge(D DSource, N Target, D DTarget) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) - : Target(std::move(Target)), DSource(std::move(DSource)), + : DSource(std::move(DSource)), Target(std::move(Target)), DTarget(std::move(DTarget)) {} - [[nodiscard]] ByConstRef getTarget() const noexcept { return Target; } - [[nodiscard]] ByConstRef factAtSource() const noexcept { return DSource; } + [[nodiscard]] ByConstRef getTarget() const noexcept { return Target; } + [[nodiscard]] ByConstRef factAtTarget() const noexcept { return DTarget; } + [[nodiscard]] std::tuple, ByConstRef, ByConstRef> + get() const noexcept { + return {DSource, Target, DTarget}; + } + + [[nodiscard]] std::tuple consume() noexcept { + return {std::move(DSource), std::move(Target), std::move(DTarget)}; + } + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const PathEdge &Edge) { return OS << "<" << Edge.DSource << "> -> <" << Edge.Target << "," @@ -40,8 +49,8 @@ template class PathEdge { } private: - N Target; D DSource; + N Target; D DTarget; }; diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 85f9aade7..65da6d5e8 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -14,66 +14,231 @@ * Author: rleer */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_SOLVERRESULTS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_SOLVERRESULTS_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVERRESULTS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVERRESULTS_H #include "phasar/Domain/BinaryDomain.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/Printer.h" #include "phasar/Utils/Table.h" +#include "phasar/Utils/Utilities.h" #include #include #include #include -namespace psr { +namespace llvm { +class Instruction; +class Value; +} // namespace llvm -template class SolverResults { -private: - Table &Results; - D ZV; +namespace psr { +// For sorting the results in dumpResults() +std::string getMetaDataID(const llvm::Value *V); +namespace detail { +template +class SolverResultsBase { public: - SolverResults(Table &ResTab, D ZV) : Results(ResTab), ZV(ZV) {} + using n_t = N; + using d_t = D; + using l_t = L; - ByConstRef resultAt(ByConstRef Stmt, ByConstRef Node) const { - return Results.get(Stmt, Node); + [[nodiscard]] ByConstRef resultAt(ByConstRef Stmt, + ByConstRef Node) const { + return self().Results.get(Stmt, Node); } - std::unordered_map resultsAt(ByConstRef Stmt, - bool StripZero = false) const { - std::unordered_map Result = Results.row(Stmt); + [[nodiscard]] std::unordered_map resultsAt(ByConstRef Stmt, + bool StripZero) const { + std::unordered_map Result = self().Results.row(Stmt); if (StripZero) { - for (auto It = Result.begin(); It != Result.end();) { - if (It->first == ZV) { - It = Result.erase(It); - } else { - ++It; - } - } + Result.erase(self().ZV); } return Result; } + [[nodiscard]] const std::unordered_map & + resultsAt(ByConstRef Stmt) const { + return self().Results.row(Stmt); + } + // this function only exists for IFDS problems which use BinaryDomain as their // value domain L - template >> - std::set ifdsResultsAt(ByConstRef Stmt) const { + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { std::set KeySet; - std::unordered_map ResultMap = this->resultsAt(Stmt); - for (auto FlowFact : ResultMap) { - KeySet.insert(FlowFact.first); + const auto &ResultMap = self().Results.row(Stmt); + for (const auto &[FlowFact, Val] : ResultMap) { + KeySet.insert(FlowFact); } return KeySet; } - [[nodiscard]] std::vector::Cell> + /// Returns the data-flow results at the given statement while respecting + /// LLVM's SSA semantics. + /// + /// An example: when a value is loaded and the location loaded from, here + /// variable 'i', is a data-flow fact that holds, then the loaded value '%0' + /// will usually be generated and also holds. However, due to the underlying + /// theory (and respective implementation) this load instruction causes the + /// loaded value to be generated and thus, it will be valid only AFTER the + /// load instruction, i.e., at the successor instruction. + /// + /// %0 = load i32, i32* %i, align 4 + /// + /// This result accessor function returns the results at the successor + /// instruction(s) reflecting that the expression on the left-hand side holds + /// if the expression on the right-hand side holds. + template + [[nodiscard]] typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + std::unordered_map> + resultsAtInLLVMSSA(ByConstRef Stmt, bool AllowOverapproximation = false, + bool StripZero = false) const; + + /// Returns the L-type result at the given statement for the given data-flow + /// fact while respecting LLVM's SSA semantics. + /// + /// An example: when a value is loaded and the location loaded from, here + /// variable 'i', is a data-flow fact that holds, then the loaded value '%0' + /// will usually be generated and also holds. However, due to the underlying + /// theory (and respective implementation) this load instruction causes the + /// loaded value to be generated and thus, it will be valid only AFTER the + /// load instruction, i.e., at the successor instruction. + /// + /// %0 = load i32, i32* %i, align 4 + /// + /// This result accessor function returns the results at the successor + /// instruction(s) reflecting that the expression on the left-hand side holds + /// if the expression on the right-hand side holds. + template + [[nodiscard]] typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + l_t> + resultAtInLLVMSSA(ByConstRef Stmt, d_t Value, + bool AllowOverapproximation = false) const; + + [[nodiscard]] std::vector::Cell> getAllResultEntries() const { - return Results.cellVec(); + return self().Results.cellVec(); + } + + template + void dumpResults(const ICFGTy &ICF, + llvm::raw_ostream &OS = llvm::outs()) const { + using f_t = typename ICFGTy::f_t; + + PAMM_GET_INSTANCE; + START_TIMER("DFA IDE Result Dumping", Full); + OS << "\n***************************************************************\n" + << "* Raw IDESolver results *\n" + << "***************************************************************\n"; + auto Cells = self().Results.cellVec(); + if (Cells.empty()) { + OS << "No results computed!" << '\n'; + } else { + std::sort( + Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { + if constexpr (std::is_same_v) { + return StringIDLess{}(getMetaDataID(Lhs.getRowKey()), + getMetaDataID(Rhs.getRowKey())); + } else { + // If non-LLVM IR is used + return Lhs.getRowKey() < Rhs.getRowKey(); + } + }); + n_t Prev = n_t{}; + n_t Curr = n_t{}; + f_t PrevFn = f_t{}; + f_t CurrFn = f_t{}; + for (unsigned I = 0; I < Cells.size(); ++I) { + Curr = Cells[I].getRowKey(); + CurrFn = ICF.getFunctionOf(Curr); + if (PrevFn != CurrFn) { + PrevFn = CurrFn; + OS << "\n\n============ Results for function '" + + ICF.getFunctionName(CurrFn) + "' ============\n"; + } + if (Prev != Curr) { + Prev = Curr; + std::string NString = NToString(Curr); + std::string Line(NString.size(), '-'); + OS << "\n\nN: " << NString << "\n---" << Line << '\n'; + } + OS << "\tD: " << DToString(Cells[I].getColumnKey()) + << " | V: " << LToString(Cells[I].getValue()) << '\n'; + } + } + OS << '\n'; + STOP_TIMER("DFA IDE Result Dumping", Full); + } + +private: + [[nodiscard]] const Derived &self() const noexcept { + static_assert(std::is_base_of_v); + return static_cast(*this); } }; +} // namespace detail + +template +class SolverResults + : public detail::SolverResultsBase, N, D, L> { + using base_t = detail::SolverResultsBase, N, D, L>; + friend base_t; + +public: + using typename base_t::d_t; + using typename base_t::l_t; + using typename base_t::n_t; + + SolverResults(const Table &ResTab, ByConstRef ZV) noexcept + : Results(ResTab), ZV(ZV) {} + SolverResults(Table &&ResTab, ByConstRef ZV) = delete; + +private: + const Table &Results; + ByConstRef ZV; +}; + +template +class OwningSolverResults + : public detail::SolverResultsBase, N, D, L> { + using base_t = + detail::SolverResultsBase, N, D, L>; + friend base_t; + +public: + using typename base_t::d_t; + using typename base_t::l_t; + using typename base_t::n_t; + + OwningSolverResults(Table ResTab, + D ZV) noexcept(std::is_nothrow_move_constructible_v) + : Results(std::move(ResTab)), ZV(std::move(ZV)) {} + + [[nodiscard]] SolverResults get() const &noexcept { + return {Results, ZV}; + } + SolverResults get() && = delete; + + [[nodiscard]] operator SolverResults() const &noexcept { + return get(); + } + + operator SolverResults() && = delete; + +private: + Table Results; + D ZV; +}; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h index d02851edf..3154c52b3 100644 --- a/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h +++ b/include/phasar/DataFlow/IfdsIde/SpecialSummaries.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SPECIALSUMMARIES_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SPECIALSUMMARIES_H +#ifndef PHASAR_DATAFLOW_IFDSIDE_SPECIALSUMMARIES_H +#define PHASAR_DATAFLOW_IFDSIDE_SPECIALSUMMARIES_H #include "phasar/Config/Configuration.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" diff --git a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h index 07a7693c7..2e835c6f5 100644 --- a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h +++ b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h @@ -1,5 +1,5 @@ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_CONTEXTS_CALLSTRINGCTX_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_CONTEXTS_CALLSTRINGCTX_H +#ifndef PHASAR_DATAFLOW_MONO_CONTEXTS_CALLSTRINGCTX_H +#define PHASAR_DATAFLOW_MONO_CONTEXTS_CALLSTRINGCTX_H #include "phasar/Utils/Printer.h" @@ -68,11 +68,10 @@ template class CallStringCTX { return Lhs.cs < Rhs.cs; } - llvm::raw_ostream &print(llvm::raw_ostream &OS, - const NodePrinterBase &NP) const { + llvm::raw_ostream &print(llvm::raw_ostream &OS) const { OS << "Call string: [ "; for (auto C : CallString) { - NP.printNode(OS, C); + OS << NToString(C); if (C != CallString.back()) { OS << " * "; } diff --git a/include/phasar/DataFlow/Mono/InterMonoProblem.h b/include/phasar/DataFlow/Mono/InterMonoProblem.h index 5d119ac64..9ee1b3298 100644 --- a/include/phasar/DataFlow/Mono/InterMonoProblem.h +++ b/include/phasar/DataFlow/Mono/InterMonoProblem.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_INTERMONOPROBLEM_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_INTERMONOPROBLEM_H +#ifndef PHASAR_DATAFLOW_MONO_INTERMONOPROBLEM_H +#define PHASAR_DATAFLOW_MONO_INTERMONOPROBLEM_H #include "phasar/ControlFlow/ICFGBase.h" #include "phasar/DataFlow/Mono/IntraMonoProblem.h" diff --git a/include/phasar/DataFlow/Mono/IntraMonoProblem.h b/include/phasar/DataFlow/Mono/IntraMonoProblem.h index 4f5927d81..4915cfe4f 100644 --- a/include/phasar/DataFlow/Mono/IntraMonoProblem.h +++ b/include/phasar/DataFlow/Mono/IntraMonoProblem.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_INTRAMONOPROBLEM_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_INTRAMONOPROBLEM_H +#ifndef PHASAR_DATAFLOW_MONO_INTRAMONOPROBLEM_H +#define PHASAR_DATAFLOW_MONO_INTRAMONOPROBLEM_H #include "phasar/ControlFlow/CFGBase.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -35,10 +35,7 @@ struct HasNoConfigurationType; template class TypeHierarchy; template class CFG; -template -class IntraMonoProblem : public NodePrinter, - public DataFlowFactPrinter, - public FunctionPrinter { +template class IntraMonoProblem { public: using n_t = typename AnalysisDomainTy::n_t; using d_t = typename AnalysisDomainTy::d_t; @@ -72,7 +69,7 @@ class IntraMonoProblem : public NodePrinter, : IRDB(IRDB), TH(TH), CF(CF), PT(PT), EntryPoints(std::move(EntryPoints)) {} - ~IntraMonoProblem() override = default; + virtual ~IntraMonoProblem() = default; virtual mono_container_t normalFlow(n_t Inst, const mono_container_t &In) = 0; diff --git a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h index c1a7285b6..4e05c9279 100644 --- a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_SOLVER_INTERMONOSOLVER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_SOLVER_INTERMONOSOLVER_H +#ifndef PHASAR_DATAFLOW_MONO_SOLVER_INTERMONOSOLVER_H +#define PHASAR_DATAFLOW_MONO_SOLVER_INTERMONOSOLVER_H #include "phasar/DataFlow/Mono/Contexts/CallStringCTX.h" #include "phasar/DataFlow/Mono/InterMonoProblem.h" @@ -85,15 +85,14 @@ template class InterMonoSolver { void printWorkList() { llvm::outs() << "CURRENT WORKLIST:\n"; for (auto &[Src, Dst] : Worklist) { - llvm::outs() << IMProblem.NtoString(Src) << " --> " - << IMProblem.NtoString(Dst) << '\n'; + llvm::outs() << NToString(Src) << " --> " << NToString(Dst) << '\n'; } llvm::outs() << "-----------------\n"; } void addCalleesToWorklist(std::pair Edge) { auto Src = Edge.first; - auto Dst = Edge.second; + // auto Dst = Edge.second; // Add inter- and intra-edges of callee(s) for (auto Callee : ICF->getCalleesOfCallAt(Src)) { if (AddedFunctions.find(Callee) != AddedFunctions.end()) { @@ -178,8 +177,8 @@ template class InterMonoSolver { llvm::outs() << "Handle normal flow\n"; auto Src = Edge.first; auto Dst = Edge.second; - llvm::outs() << "Src: " << IMProblem.NtoString(Src) << '\n'; - llvm::outs() << "Dst: " << IMProblem.NtoString(Dst) << '\n'; + llvm::outs() << "Src: " << NToString(Src) << '\n'; + llvm::outs() << "Dst: " << NToString(Dst) << '\n'; std::unordered_map, mono_container_t> Out; for (auto &[Ctx, Facts] : Analysis[Src]) { Out[Ctx] = IMProblem.normalFlow(Src, Analysis[Src][Ctx]); @@ -224,8 +223,8 @@ template class InterMonoSolver { std::unordered_map, mono_container_t> Out; if (!isIntraEdge(Edge)) { llvm::outs() << "Handle call flow\n"; - llvm::outs() << "Src: " << IMProblem.NtoString(Src) << '\n'; - llvm::outs() << "Dst: " << IMProblem.NtoString(Dst) << '\n'; + llvm::outs() << "Src: " << NToString(Src) << '\n'; + llvm::outs() << "Dst: " << NToString(Dst) << '\n'; for (auto &[Ctx, Facts] : Analysis[Src]) { auto CTXAdd(Ctx); CTXAdd.push_back(Src); @@ -253,8 +252,8 @@ template class InterMonoSolver { } else { // Handle call-to-ret flow llvm::outs() << "Handle call to ret flow\n"; - llvm::outs() << "Src: " << IMProblem.NtoString(Src) << '\n'; - llvm::outs() << "Dst: " << IMProblem.NtoString(Dst) << '\n'; + llvm::outs() << "Src: " << NToString(Src) << '\n'; + llvm::outs() << "Dst: " << NToString(Dst) << '\n'; for (auto &[Ctx, Facts] : Analysis[Src]) { // call-to-ret flow does not modify contexts Out[Ctx] = IMProblem.callToRetFlow( @@ -288,11 +287,11 @@ template class InterMonoSolver { std::unordered_map, mono_container_t> Out; llvm::outs() << "\nHandle ret flow in: " << ICF->getFunctionName(ICF->getFunctionOf(Src)) << '\n'; - llvm::outs() << "Src: " << IMProblem.NtoString(Src) << '\n'; - llvm::outs() << "Dst: " << IMProblem.NtoString(Dst) << '\n'; + llvm::outs() << "Src: " << NToString(Src) << '\n'; + llvm::outs() << "Dst: " << NToString(Dst) << '\n'; for (auto &[Ctx, Facts] : Analysis[Src]) { auto CTXRm(Ctx); - CTXRm.print(llvm::outs() << "CTXRm: ", IMProblem) << '\n'; + CTXRm.print(llvm::outs() << "CTXRm: ") << '\n'; // we need to use several call- and retsites if the context is empty llvm::SmallVector CallSites; @@ -319,7 +318,7 @@ template class InterMonoSolver { // TODO! llvm::outs() << "ResSites.size(): " << RetSites.size() << '\n'; for (auto RetSite : RetSites) { - llvm::outs() << "RetSite: " << IMProblem.NtoString(RetSite) << '\n'; + llvm::outs() << "RetSite: " << NToString(RetSite) << '\n'; llvm::outs() << "Return facts: "; IMProblem.printContainer(llvm::outs(), Out[CTXRm]); llvm::outs() << '\n'; @@ -363,7 +362,7 @@ template class InterMonoSolver { std::pair Edge = Worklist.front(); Worklist.pop_front(); auto Src = Edge.first; - auto Dst = Edge.second; + // auto Dst = Edge.second; if (ICF->isCallSite(Src)) { addCalleesToWorklist(Edge); } @@ -402,19 +401,17 @@ template class InterMonoSolver { virtual void dumpResults(llvm::raw_ostream &OS = llvm::outs()) { OS << "======= DUMP LLVM-INTER-MONOTONE-SOLVER RESULTS =======\n"; for (auto &[Node, ContextMap] : this->Analysis) { - OS << "Instruction:\n" << IMProblem.NtoString(Node); + OS << "Instruction:\n" << NToString(Node); OS << "\nFacts:\n"; if (ContextMap.empty()) { OS << "\tEMPTY\n"; } else { for (auto &[Context, FlowFacts] : ContextMap) { - Context.print(OS, IMProblem) << '\n'; + Context.print(OS) << '\n'; if (FlowFacts.empty()) { OS << "\tEMPTY\n"; } else { - for (auto FlowFact : FlowFacts) { - OS << IMProblem.DtoString(FlowFact); - } + IMProblem.printContainer(OS, FlowFacts); } } } diff --git a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h index 0283ce9d4..ac46b9068 100644 --- a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_SOLVER_INTRAMONOSOLVER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_SOLVER_INTRAMONOSOLVER_H +#ifndef PHASAR_DATAFLOW_MONO_SOLVER_INTRAMONOSOLVER_H +#define PHASAR_DATAFLOW_MONO_SOLVER_INTRAMONOSOLVER_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" #include "phasar/Utils/BitVectorSet.h" @@ -115,14 +115,12 @@ template class IntraMonoSolver { OS << "Intra-Monotone solver results:\n" "------------------------------\n"; for (auto &[Node, FlowFacts] : this->Analysis) { - OS << "Instruction:\n" << this->IMProblem.NtoString(Node); + OS << "Instruction:\n" << NToString(Node); OS << "\nFacts:\n"; if (FlowFacts.empty()) { OS << "\tEMPTY\n"; } else { - for (auto FlowFact : FlowFacts) { - OS << this->IMProblem.DtoString(FlowFact) << '\n'; - } + IMProblem.printContainer(OS, FlowFacts); } OS << "\n\n"; } diff --git a/include/phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h b/include/phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h new file mode 100644 index 000000000..df9d1ec3c --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h @@ -0,0 +1,382 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_EXPLODEDSUPERGRAPH_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_EXPLODEDSUPERGRAPH_H + +#include "phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/StableVector.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Sequence.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/TinyPtrVector.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_os_ostream.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace psr { + +/// An explicit representation of the ExplodedSuperGraph (ESG) of an IFDS/IDE +/// analysis. +/// +/// Not all covered instructions of a BasicBlock might be present; however, it +/// is guaranteed that for each BasicBlock covered by the analysis there is at +/// least one node in the ExplicitESG containing an instruction from that BB. +template class ExplodedSuperGraph { +public: + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + + struct Node { + static constexpr size_t NoPredId = ~size_t(0); + }; + + struct NodeData { + d_t Value{}; + n_t Source{}; + }; + + struct NodeAdj { + size_t PredecessorIdx = Node::NoPredId; + llvm::SmallVector Neighbors{}; + }; + + class BuildNodeRef; + class NodeRef { + friend ExplodedSuperGraph; + friend class BuildNodeRef; + + public: + NodeRef() noexcept = default; + NodeRef(std::nullptr_t) noexcept {} + + [[nodiscard]] ByConstRef value() const noexcept { + assert(*this); + return Owner->NodeDataOwner[NodeId].Value; + } + + [[nodiscard]] ByConstRef source() const noexcept { + assert(*this); + return Owner->NodeDataOwner[NodeId].Source; + } + + [[nodiscard]] NodeRef predecessor() const noexcept { + assert(*this); + auto PredId = Owner->NodeAdjOwner[NodeId].PredecessorIdx; + return PredId == Node::NoPredId ? NodeRef() : NodeRef(PredId, Owner); + } + + [[nodiscard]] bool hasNeighbors() const noexcept { + assert(*this); + return !Owner->NodeAdjOwner[NodeId].Neighbors.empty(); + } + + [[nodiscard]] bool getNumNeighbors() const noexcept { + assert(*this); + return Owner->NodeAdjOwner[NodeId].Neighbors.size(); + } + + [[nodiscard]] auto neighbors() const noexcept { + assert(*this); + + return llvm::map_range(Owner->NodeAdjOwner[NodeId].Neighbors, + [Owner{Owner}](size_t NBIdx) { + assert(NBIdx != Node::NoPredId); + return NodeRef(NBIdx, Owner); + }); + } + + [[nodiscard]] size_t id() const noexcept { return NodeId; } + + explicit operator bool() const noexcept { + return Owner != nullptr && NodeId != Node::NoPredId; + } + + [[nodiscard]] friend bool operator==(NodeRef L, NodeRef R) noexcept { + return L.NodeId == R.NodeId && L.Owner == R.Owner; + } + [[nodiscard]] friend bool operator!=(NodeRef L, NodeRef R) noexcept { + return !(L == R); + } + [[nodiscard]] friend bool operator==(NodeRef L, + std::nullptr_t /*R*/) noexcept { + return L.Owner == nullptr; + } + [[nodiscard]] friend bool operator!=(NodeRef L, std::nullptr_t R) noexcept { + return !(L == R); + } + + friend llvm::hash_code hash_value(NodeRef NR) noexcept { // NOLINT + return llvm::hash_combine(NR.NodeId, NR.Owner); + } + + private: + explicit NodeRef(size_t NodeId, const ExplodedSuperGraph *Owner) noexcept + : NodeId(NodeId), Owner(Owner) {} + + size_t NodeId = Node::NoPredId; + const ExplodedSuperGraph *Owner{}; + }; + + class BuildNodeRef { + public: + [[nodiscard]] NodeRef operator()(size_t NodeId) const noexcept { + return NodeRef(NodeId, Owner); + } + + private: + explicit BuildNodeRef(const ExplodedSuperGraph *Owner) noexcept + : Owner(Owner) {} + + const ExplodedSuperGraph *Owner{}; + }; + + explicit ExplodedSuperGraph(d_t ZeroValue) noexcept( + std::is_nothrow_move_constructible_v) + : ZeroValue(std::move(ZeroValue)) {} + + explicit ExplodedSuperGraph(const ExplodedSuperGraph &) = default; + ExplodedSuperGraph &operator=(const ExplodedSuperGraph &) = delete; + + ExplodedSuperGraph(ExplodedSuperGraph &&) noexcept = default; + ExplodedSuperGraph &operator=(ExplodedSuperGraph &&) noexcept = default; + + ~ExplodedSuperGraph() = default; + + [[nodiscard]] NodeRef getNodeOrNull(n_t Inst, d_t Fact) const { + auto It = FlowFactVertexMap.find( + std::make_pair(std::move(Inst), std::move(Fact))); + if (It != FlowFactVertexMap.end()) { + return NodeRef(It->second, this); + } + return nullptr; + } + + [[nodiscard]] NodeRef fromNodeId(size_t NodeId) const noexcept { + assert(NodeDataOwner.size() == NodeAdjOwner.size()); + assert(NodeId < NodeDataOwner.size()); + + return NodeRef(NodeId, this); + } + + [[nodiscard]] ByConstRef getZeroValue() const noexcept { + return ZeroValue; + } + + template + void saveEdges(n_t Curr, d_t CurrNode, n_t Succ, const Container &SuccNodes, + ESGEdgeKind Kind) { + auto PredId = getNodeIdOrNull(Curr, std::move(CurrNode)); + + /// The Identity CTR-flow on the zero-value has no meaning at all regarding + /// path sensitivity, so skip it + bool MaySkipEdge = Kind == ESGEdgeKind::CallToRet && CurrNode == ZeroValue; + for (const d_t &SuccNode : SuccNodes) { + saveEdge(PredId, Curr, CurrNode, Succ, SuccNode, MaySkipEdge); + } + } + + // NOLINTNEXTLINE(readability-identifier-naming) + [[nodiscard]] auto node_begin() const noexcept { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + return llvm::map_iterator( + llvm::seq(size_t(0), NodeDataOwner.size()).begin(), BuildNodeRef(this)); + } + // NOLINTNEXTLINE(readability-identifier-naming) + [[nodiscard]] auto node_end() const noexcept { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + return llvm::map_iterator(llvm::seq(size_t(0), NodeDataOwner.size()).end(), + BuildNodeRef(this)); + } + [[nodiscard]] auto nodes() const noexcept { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + return llvm::map_range(llvm::seq(size_t(0), NodeDataOwner.size()), + BuildNodeRef(this)); + } + + [[nodiscard]] size_t size() const noexcept { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + return NodeDataOwner.size(); + } + + /// Printing: + + void printAsDot(llvm::raw_ostream &OS) const { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + OS << "digraph ESG{\n"; + psr::scope_exit ClosingBrace = [&OS] { OS << '}'; }; + + for (size_t I = 0, End = NodeDataOwner.size(); I != End; ++I) { + auto Nod = NodeRef(I, this); + OS << I << "[label=\""; + OS.write_escaped(DToString(Nod.value())) << "\"];\n"; + + OS << I << "->" << intptr_t(Nod.predecessor().id()) + << R"([style="bold" label=")"; + OS.write_escaped(NToString(Nod.source())) << "\"];\n"; + for (auto NB : Nod.neighbors()) { + OS << I << "->" << NB.id() << "[color=\"red\"];\n"; + } + } + } + + void printAsDot(std::ostream &OS) const { + llvm::raw_os_ostream ROS(OS); + printAsDot(ROS); + } + + void printESGNodes(llvm::raw_ostream &OS) const { + for (const auto &[Node, _] : FlowFactVertexMap) { + OS << "( " << NToString(Node.first) << "; " << DToString(Node.second) + << " )\n"; + } + } + +private: + struct PathInfoHash { + size_t operator()(const std::pair &ND) const { + return std::hash()(ND.first) * 31 + std::hash()(ND.second); + } + }; + + struct PathInfoEq { + bool operator()(const std::pair &Lhs, + const std::pair &Rhs) const { + return Lhs.first == Rhs.first && Lhs.second == Rhs.second; + } + }; + + [[nodiscard]] std::optional getNodeIdOrNull(n_t Inst, + d_t Fact) const { + auto It = FlowFactVertexMap.find( + std::make_pair(std::move(Inst), std::move(Fact))); + if (It != FlowFactVertexMap.end()) { + return It->second; + } + return std::nullopt; + } + + void saveEdge(std::optional PredId, n_t Curr, d_t CurrNode, n_t Succ, + d_t SuccNode, bool MaySkipEdge) { + auto [SuccVtxIt, Inserted] = FlowFactVertexMap.try_emplace( + std::make_pair(Succ, SuccNode), Node::NoPredId); + + // Save a reference into the FlowFactVertexMap before the SuccVtxIt gets + // invalidated + auto &SuccVtxNode = SuccVtxIt->second; + + // NOLINTNEXTLINE(readability-identifier-naming) + auto makeNode = [this, PredId, Curr, &CurrNode, &SuccNode]() mutable { + assert(NodeAdjOwner.size() == NodeDataOwner.size()); + auto Ret = NodeDataOwner.size(); + + auto &NodData = NodeDataOwner.emplace_back(); + auto &NodAdj = NodeAdjOwner.emplace_back(); + NodData.Value = SuccNode; + + if (!PredId) { + // For the seeds: Just that the FlowFactVertexMap is filled at that + // position... + FlowFactVertexMap[std::make_pair(Curr, CurrNode)] = Ret; + } + + NodAdj.PredecessorIdx = PredId.value_or(Node::NoPredId); + NodData.Source = Curr; + + return Ret; + }; + + if (MaySkipEdge && SuccNode == CurrNode) { + // This CTR edge carries no information, so skip it. + // We still want to create the destination node for the ret-FF later + assert(PredId); + if (Inserted) { + SuccVtxNode = makeNode(); + NodeAdjOwner.back().PredecessorIdx = Node::NoPredId; + } + return; + } + + if (PredId && NodeDataOwner[*PredId].Value == SuccNode && + NodeDataOwner[*PredId].Source->getParent() == Succ->getParent() && + SuccNode != ZeroValue) { + + // Identity edge, we don't need a new node; just assign the Pred here + if (Inserted) { + SuccVtxNode = *PredId; + return; + } + + // This edge has already been here?! + if (*PredId == SuccVtxNode) { + return; + } + } + + if (Inserted) { + SuccVtxNode = makeNode(); + return; + } + + // Node has already been created, but MaySkipEdge above prevented us from + // connecting with the pred. Now, we have a non-skippable edge to connect to + NodeRef SuccVtx(SuccVtxNode, this); + if (!SuccVtx.predecessor()) { + NodeAdjOwner[SuccVtxNode].PredecessorIdx = + PredId.value_or(Node::NoPredId); + NodeDataOwner[SuccVtxNode].Source = Curr; + return; + } + + // This node has more than one predecessor; add a neighbor then + if (SuccVtx.predecessor().id() != PredId.value_or(Node::NoPredId) && + llvm::none_of(SuccVtx.neighbors(), + [Pred = PredId.value_or(Node::NoPredId)](NodeRef Nd) { + return Nd.predecessor().id() == Pred; + })) { + + auto NewNode = makeNode(); + NodeAdjOwner[SuccVtxNode].Neighbors.push_back(NewNode); + return; + } + } + + std::vector NodeDataOwner; + std::vector NodeAdjOwner; + std::unordered_map, size_t, PathInfoHash, PathInfoEq> + FlowFactVertexMap{}; + + // ZeroValue + d_t ZeroValue; +}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_EXPLODEDSUPERGRAPH_H diff --git a/include/phasar/DataFlow/PathSensitivity/FlowPath.h b/include/phasar/DataFlow/PathSensitivity/FlowPath.h new file mode 100644 index 000000000..532852ab0 --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/FlowPath.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_FLOWPATH_H +#define PHASAR_PHASARLLVM_DATAFLOW_PATHSENSITIVITY_FLOWPATH_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallVector.h" + +#include "z3++.h" + +namespace psr { +template struct FlowPath { + llvm::SmallVector Path; + z3::expr Constraint; + z3::model Model; + + FlowPath(llvm::ArrayRef Path, const z3::expr &Constraint) + : Path(Path.begin(), Path.end()), Constraint(Constraint), + Model(Constraint.ctx()) {} + FlowPath(llvm::ArrayRef Path, const z3::expr &Constraint, + const z3::model &Model) + : Path(Path.begin(), Path.end()), Constraint(Constraint), Model(Model) {} + + [[nodiscard]] auto begin() noexcept { return Path.begin(); } + [[nodiscard]] auto end() noexcept { return Path.end(); } + [[nodiscard]] auto begin() const noexcept { return Path.begin(); } + [[nodiscard]] auto end() const noexcept { return Path.end(); } + [[nodiscard]] auto cbegin() const noexcept { return Path.cbegin(); } + [[nodiscard]] auto cend() const noexcept { return Path.cend(); } + + [[nodiscard]] size_t size() const noexcept { return Path.size(); } + [[nodiscard]] bool empty() const noexcept { return Path.empty(); } + + [[nodiscard]] decltype(auto) operator[](size_t Idx) const { + return Path[Idx]; + } + + [[nodiscard]] operator llvm::ArrayRef() const noexcept { return Path; } + + [[nodiscard]] bool operator==(const FlowPath &Other) const noexcept { + return Other.Path == Path; + } + [[nodiscard]] bool operator!=(const FlowPath &Other) const noexcept { + return !(*this == Other); + } +}; + +template using FlowPathSequence = std::vector>; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOW_PATHSENSITIVITY_FLOWPATH_H diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h new file mode 100644 index 000000000..582dd02b1 --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYCONFIG_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYCONFIG_H + +#include +#include + +namespace psr { + +template struct PathSensitivityConfigBase { + size_t DAGSizeThreshold = SIZE_MAX; + size_t DAGDepthThreshold = SIZE_MAX; + size_t NumPathsThreshold = SIZE_MAX; + bool MinimizeDAG = true; + + [[nodiscard]] DerivedConfig + withDAGSizeThreshold(size_t MaxDAGSize) const noexcept { + auto Ret = *static_cast(this); + Ret.DAGSizeThreshold = MaxDAGSize; + return Ret; + } + + [[nodiscard]] DerivedConfig + withDAGDepthThreshold(size_t MaxDAGDepth) const noexcept { + auto Ret = *static_cast(this); + Ret.DAGDepthThreshold = MaxDAGDepth; + return Ret; + } + + [[nodiscard]] DerivedConfig + withNumPathsThreshold(size_t MaxNumPaths) const noexcept { + auto Ret = *static_cast(this); + Ret.NumPathsThreshold = MaxNumPaths; + return Ret; + } + + [[nodiscard]] DerivedConfig withMinimizeDAG(bool DoMinimize) const noexcept { + auto Ret = *static_cast(this); + Ret.MinimizeDAG = DoMinimize; + return Ret; + } +}; + +struct PathSensitivityConfig + : PathSensitivityConfigBase {}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYCONFIG_H diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h new file mode 100644 index 000000000..d781d127f --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManager.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGER_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGER_H + +#include "phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h" +#include "phasar/Utils/AdjacencyList.h" +#include "phasar/Utils/DFAMinimizer.h" +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/Logger.h" + +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" + +#include +#include + +namespace psr { + +template +class PathSensitivityManager + : public PathSensitivityManagerBase, + public PathSensitivityManagerMixin< + PathSensitivityManager, AnalysisDomainTy, + typename PathSensitivityManagerBase< + typename AnalysisDomainTy::n_t>::graph_type> { + using base_t = PathSensitivityManagerBase; + using mixin_t = + PathSensitivityManagerMixin; + +public: + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using typename PathSensitivityManagerBase::graph_type; + + PathSensitivityManager( + const ExplodedSuperGraph *ESG) noexcept + : mixin_t(ESG) {} +}; +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGER_H diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h new file mode 100644 index 000000000..e1195bb57 --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h @@ -0,0 +1,191 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERBASE_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERBASE_H + +#include "phasar/Utils/AdjacencyList.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/IntEqClasses.h" +#include "llvm/ADT/SmallVector.h" + +namespace llvm { +class Instruction; +} // namespace llvm + +namespace psr { + +template +class PathSensitivityManagerMixin; + +template class PathSensitivityManagerBase { +public: + using n_t = N; + using graph_type = AdjacencyList>; + + static_assert(std::is_integral_v::vertex_t>); + + template + friend class PathSensitivityManagerMixin; + +protected: + using graph_traits_t = GraphTraits; + using vertex_t = typename graph_traits_t::vertex_t; + +private: + [[nodiscard]] bool assertIsDAG(const graph_type &Dag) const { + llvm::BitVector Visited(graph_traits_t::size(Dag)); + llvm::DenseSet CurrPath; + CurrPath.reserve(graph_traits_t::size(Dag)); + + // NOLINTNEXTLINE(readability-identifier-naming) + auto doAssertIsDAG = [&CurrPath, &Visited, &Dag](auto &doAssertIsDAG, + vertex_t Vtx) { + if (!CurrPath.insert(Vtx).second) { + PHASAR_LOG_LEVEL(ERROR, "DAG has circle: Vtx: " << uintptr_t(Vtx)); + return false; + } + + scope_exit CurrPathPop = [&CurrPath, Vtx] { CurrPath.erase(Vtx); }; + if (Visited.test(Vtx)) { + /// We have already analyzed this node + /// NOTE: We must check this _after_ doing the circle check. Otherwise, + /// that can never be true + return true; + } + + Visited.set(Vtx); + + for (auto Succ : graph_traits_t::outEdges(Dag, Vtx)) { + if (!doAssertIsDAG(doAssertIsDAG, Succ)) { + return false; + } + } + return true; + }; + + for (auto Rt : graph_traits_t::roots(Dag)) { + if (!doAssertIsDAG(doAssertIsDAG, Rt)) { + return false; + } + } + + return true; + } + + template + [[nodiscard]] static graph_type + reverseDAG(graph_type &&Dag, const VertexTransform &Equiv, size_t EquivSize, + size_t MaxDepth) { + using graph_traits_t = psr::GraphTraits; + using vertex_t = typename graph_traits_t::vertex_t; + graph_type Ret{}; + if constexpr (psr::is_reservable_graph_trait_v) { + graph_traits_t::reserve(Ret, EquivSize); + } + + // Allocate a buffer for the temporary data needed. + // We need: + // - A cache of vertex_t + // - One worklist WLConsume of pair where we read from + // - One worklist WLInsert of same type where we insert to + + // We iterate over the graph in levels. Each level corresponds to the + // distance from a root node. We always have all nodes from a level inside a + // worklist + // -- The level we are processing is in WLConsume, the next level in + // WLInsert. This way, we can stop the process, when we have reached the + // MaxDepth + + constexpr auto Factor = + sizeof(vertex_t) + 2 * sizeof(std::pair); + assert(EquivSize <= SIZE_MAX / Factor && "Overflow on size calculation"); + auto NumBytes = Factor * EquivSize; + + // For performance reasons, we wish to allocate the buffer on the stack, if + // it is small enough + static constexpr size_t MaxNumBytesInStackBuf = 8192; + + auto *Buf = NumBytes <= MaxNumBytesInStackBuf + ? reinterpret_cast(alloca(NumBytes)) + : new char[NumBytes]; + std::unique_ptr Owner; // NOLINT + if (NumBytes > MaxNumBytesInStackBuf) { + Owner.reset(Buf); + } + + auto Cache = reinterpret_cast(Buf); + std::uninitialized_fill_n(Cache, EquivSize, graph_traits_t::Invalid); + + auto *WLConsumeBegin = + reinterpret_cast *>(Cache + EquivSize); + auto *WLConsumeEnd = WLConsumeBegin; + auto *WLInsertBegin = WLConsumeBegin + EquivSize; + auto *WLInsertEnd = WLInsertBegin; + + for (auto Rt : graph_traits_t::roots(Dag)) { + size_t Eq = std::invoke(Equiv, Rt); + if (Cache[Eq] == graph_traits_t::Invalid) { + Cache[Eq] = graph_traits_t::addNode(Ret, graph_traits_t::node(Dag, Rt)); + *WLConsumeEnd++ = {Rt, Cache[Eq]}; + } + } + + size_t Depth = 0; + + while (WLConsumeBegin != WLConsumeEnd && Depth < MaxDepth) { + for (auto [Vtx, Rev] : llvm::make_range(WLConsumeBegin, WLConsumeEnd)) { + + for (auto Succ : graph_traits_t::outEdges(Dag, Vtx)) { + auto SuccVtx = graph_traits_t::target(Succ); + size_t Eq = std::invoke(Equiv, SuccVtx); + if (Cache[Eq] == graph_traits_t::Invalid) { + Cache[Eq] = graph_traits_t::addNode( + Ret, graph_traits_t::node(Dag, SuccVtx)); + *WLInsertEnd++ = {SuccVtx, Cache[Eq]}; + } + + auto SuccRev = Cache[Eq]; + graph_traits_t::addEdge(Ret, SuccRev, + graph_traits_t::withEdgeTarget(Succ, Rev)); + } + if (graph_traits_t::outDegree(Dag, Vtx) == 0) { + graph_traits_t::addRoot(Ret, Rev); + } + } + + std::swap(WLConsumeBegin, WLInsertBegin); + WLConsumeEnd = std::exchange(WLInsertEnd, WLInsertBegin); + ++Depth; + } + + for (auto [Rt, RtRev] : llvm::make_range(WLConsumeBegin, WLConsumeEnd)) { + // All nodes that were cut off because they are at depth MaxDepth must + // become roots + graph_traits_t::addRoot(Ret, RtRev); + } + + return Ret; + } + + [[nodiscard]] static graph_type reverseDAG(graph_type &&Dag, + size_t MaxDepth) { + auto Sz = graph_traits_t::size(Dag); + return reverseDAG(std::move(Dag), identity{}, Sz, MaxDepth); + } +}; + +extern template class PathSensitivityManagerBase; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERBASE_H diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h new file mode 100644 index 000000000..6a986d99c --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h @@ -0,0 +1,342 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERMIXIN_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERMIXIN_H + +#include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/DataFlow/PathSensitivity/ExplodedSuperGraph.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h" +#include "phasar/DataFlow/PathSensitivity/PathTracingFilter.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/DFAMinimizer.h" +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/Printer.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +namespace llvm { +class DbgInfoIntrinsic; +} // namespace llvm + +namespace psr { +template +class PathSensitivityManagerMixin { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + + using NodeRef = typename ExplodedSuperGraph::NodeRef; + using graph_type = GraphType; + using graph_traits_t = GraphTraits; + using vertex_t = typename graph_traits_t::vertex_t; + + struct PathsToContext { + llvm::DenseMap Cache; + llvm::SetVector> CurrPath; + }; + + static const ExplodedSuperGraph & + assertNotNull(const ExplodedSuperGraph *ESG) noexcept { + assert(ESG != nullptr && "The exploded supergraph passed to the " + "pathSensitivityManager must not be nullptr!"); + return *ESG; + } + +protected: + PathSensitivityManagerMixin( + const ExplodedSuperGraph *ESG) noexcept + : ESG(assertNotNull(ESG)) { + static_assert(std::is_base_of_v, Derived>, + "Invalid CTRP instantiation: The Derived type must inherit " + "from PathSensitivityManagerBase!"); + } + +public: + template < + typename FactsRangeTy, typename ConfigTy, + typename Filter = DefaultPathTracingFilter, + typename = std::enable_if_t>> + [[nodiscard]] GraphType + pathsDagToAll(n_t Inst, FactsRangeTy FactsRange, + const PathSensitivityConfigBase &Config, + const Filter &PFilter = {}) const { + graph_type Dag; + PathsToContext Ctx; + + for (const d_t &Fact : FactsRange) { + auto Nod = ESG.getNodeOrNull(Inst, std::move(Fact)); + + if (LLVM_UNLIKELY(!Nod)) { + llvm::errs() << "Invalid Instruction-FlowFact pair. Only use those " + "pairs that are part of the IDE analysis results!\n"; + llvm::errs() << "Fatal error occurred. Writing ESG to temp file...\n"; + llvm::errs().flush(); + + auto FileName = std::string(tmpnam(nullptr)) + "-explicitesg-err.dot"; + + { + std::error_code EC; + llvm::raw_fd_ostream ROS(FileName, EC); + ESG.printAsDot(ROS); + } + + llvm::errs() << "> ESG written to " << FileName << '\n'; + llvm::errs().flush(); + + abort(); + } + + /// NOTE: We don't need to check that Nod has not been processed yet, + /// because in the ESG construction we only merge nodes with the same flow + /// fact. Here, the flow fact for each node differs (assuming FactsRage + /// does not contain duplicates) + + auto Rt = pathsToImpl(Inst, Nod, Dag, Ctx, PFilter); + if (Rt != GraphTraits::Invalid) { + graph_traits_t::addRoot(Dag, Rt); + } + } + +#ifndef NDEBUG + if (!static_cast(this)->assertIsDAG(Dag)) { + llvm::report_fatal_error("Invariant violated: DAG has a circle in it!"); + } else { + PHASAR_LOG_LEVEL_CAT(DEBUG, "PathSensitivityManager", + "The DAG indeed has no circles"); + } + +#endif + + if (Config.DAGDepthThreshold != SIZE_MAX) { + Dag = Derived::reverseDAG(std::move(Dag), Config.DAGDepthThreshold); + } else { + Dag = reverseGraph(std::move(Dag)); + } + + if (Config.MinimizeDAG) { + + auto Equiv = minimizeGraph(Dag); + + Dag = createEquivalentGraphFrom(std::move(Dag), Equiv); + +#ifndef NDEBUG + if (!static_cast(this)->assertIsDAG(Dag)) { + llvm::report_fatal_error("Invariant violated: DAG has a circle in it!"); + } else { + PHASAR_LOG_LEVEL_CAT(DEBUG, "PathSensitivityManager", + "The DAG indeed has no circles"); + } +#endif + } + + return Dag; + } + + template < + typename ConfigTy, typename L, typename Filter = DefaultPathTracingFilter, + typename = std::enable_if_t>> + [[nodiscard]] GraphType + pathsDagTo(n_t Inst, const SolverResults &SR, + const PathSensitivityConfigBase &Config, + const Filter &PFilter = {}) const { + auto Res = SR.resultsAt(Inst); + auto FactsRange = llvm::make_first_range(Res); + return pathsDagToAll(std::move(Inst), FactsRange, Config, PFilter); + } + + template < + typename ConfigTy, typename Filter = DefaultPathTracingFilter, + typename = std::enable_if_t>> + [[nodiscard]] GraphType + pathsDagTo(n_t Inst, d_t Fact, + const PathSensitivityConfigBase &Config, + const Filter &PFilter = {}) const { + + return pathsDagToAll(std::move(Inst), llvm::ArrayRef(&Fact, 1), Config, + PFilter); + } + + template < + typename ConfigTy, typename Filter = DefaultPathTracingFilter, + typename = std::enable_if_t>> + [[nodiscard]] GraphType + pathsDagToInLLVMSSA(n_t Inst, d_t Fact, + const PathSensitivityConfigBase &Config, + const Filter &PFilter = {}) const { + // Temporary code to bridge the time until merging f-IDESolverStrategy + // into development + if (Inst->getType()->isVoidTy()) { + return pathsDagToAll(Inst, llvm::ArrayRef(&Fact, 1), Config, PFilter); + } + + if (auto Next = Inst->getNextNonDebugInstruction()) { + return pathsDagToAll(Next, llvm::ArrayRef(&Fact, 1), Config, PFilter); + } + + PHASAR_LOG_LEVEL(WARNING, "[pathsDagToInLLVMSSA]: Cannot precisely " + "determine the ESG node for inst-flowfact-pair (" + << NToString(Inst) << ", " << DToString(Fact) + << "). Fall-back to an approximation"); + + for (const auto *BB : llvm::successors(Inst)) { + const auto *First = &BB->front(); + if (llvm::isa(First)) { + First = First->getNextNonDebugInstruction(); + } + if (ESG.getNodeOrNull(First, Fact)) { + return pathsDagToAll(First, llvm::ArrayRef(&Fact, 1), Config, PFilter); + } + } + + llvm::report_fatal_error("Could not determine any ESG node corresponding " + "to the inst-flowfact-pair (" + + llvm::Twine(NToString(Inst)) + ", " + + DToString(Fact) + ")"); + + return {}; + } + +private: + template + bool pathsToImplLAInvoke(vertex_t Ret, NodeRef Vtx, PathsToContext &Ctx, + graph_type &RetDag, const Filter &PFilter) const { + NodeRef Prev; + bool IsEnd = false; + bool IsError = false; + + do { + Prev = Vtx; + graph_traits_t::node(RetDag, Ret).push_back(Vtx.source()); + + Vtx = Vtx.predecessor(); + + if (PFilter.HasReachedEnd(Prev, Vtx)) { + IsEnd = true; + } else if (PFilter.IsErrorneousTransition(Prev, Vtx)) { + IsError = true; + } + + if (!Vtx) { + return !IsError; + } + + if (IsEnd || IsError) { + break; + } + + } while (!Vtx.hasNeighbors()); + + if (!Ctx.CurrPath.insert(Ret)) { + PHASAR_LOG_LEVEL(ERROR, "Node " << Ret << " already on path"); + return false; + } + scope_exit PopRet = [&Ctx] { Ctx.CurrPath.pop_back(); }; + + // NOLINTNEXTLINE(readability-identifier-naming) + auto traverseNext = [&Ctx, this, Ret, &RetDag, &PFilter](NodeRef Nxt) { + auto Succ = pathsToImplLA(Nxt, Ctx, RetDag, PFilter); + if (Succ != graph_traits_t::Invalid && !Ctx.CurrPath.contains(Succ)) { + graph_traits_t::addEdge(RetDag, Ret, Succ); + } + }; + + if (!IsEnd && !IsError) { + traverseNext(Vtx); + } + + for (auto Nxt : Vtx.neighbors()) { + assert(Nxt != nullptr); + if (PFilter.IsErrorneousTransition(Prev, Nxt)) { + continue; + } + if (PFilter.HasReachedEnd(Prev, Nxt)) { + IsEnd = true; + continue; + } + traverseNext(Nxt); + } + + graph_traits_t::dedupOutEdges(RetDag, Ret); + + return IsEnd || graph_traits_t::outDegree(RetDag, Ret) != 0; + } + + template + vertex_t pathsToImplLA(NodeRef Vtx, PathsToContext &Ctx, graph_type &RetDag, + const Filter &PFilter) const { + /// Idea: Treat the graph as firstChild-nextSibling notation and always + /// traverse with one predecessor lookAhead + + auto [It, Inserted] = + Ctx.Cache.try_emplace(Vtx.id(), graph_traits_t::Invalid); + if (!Inserted) { + return It->second; + } + + auto Ret = + graph_traits_t::addNode(RetDag, typename graph_traits_t::value_type()); + // auto Ret = RetDag.addNode(); + It->second = Ret; + + if (!pathsToImplLAInvoke(Ret, Vtx, Ctx, RetDag, PFilter)) { + /// NOTE: Don't erase Vtx from Cache to guarantee termination + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) -- fp + Ctx.Cache[Vtx.id()] = graph_traits_t::Invalid; + + if (Ctx.CurrPath.contains(Ret) || !graph_traits_t::pop(RetDag, Ret)) { + PHASAR_LOG_LEVEL(WARNING, "Cannot remove invalid path at: " << Ret); + graph_traits_t::node(RetDag, Ret).clear(); + } + + return graph_traits_t::Invalid; + } + return Ret; + } + + template + vertex_t pathsToImpl(n_t QueryInst, NodeRef Vtx, graph_type &RetDag, + PathsToContext &Ctx, const Filter &PFilter) const { + + auto Ret = + graph_traits_t::addNode(RetDag, typename graph_traits_t::value_type()); + graph_traits_t::node(RetDag, Ret).push_back(QueryInst); + + for (auto NB : Vtx.neighbors()) { + auto NBNode = pathsToImplLA(NB, Ctx, RetDag, PFilter); + if (NBNode != graph_traits_t::Invalid) { + graph_traits_t::addEdge(RetDag, Ret, NBNode); + } + } + auto VtxNode = pathsToImplLA(Vtx, Ctx, RetDag, PFilter); + if (VtxNode != graph_traits_t::Invalid) { + graph_traits_t::addEdge(RetDag, Ret, VtxNode); + } + + graph_traits_t::dedupOutEdges(RetDag, Ret); + + return Ret; + } + + const ExplodedSuperGraph &ESG; +}; +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_PATHSENSITIVITYMANAGERMIXIN_H diff --git a/include/phasar/DataFlow/PathSensitivity/PathTracingFilter.h b/include/phasar/DataFlow/PathSensitivity/PathTracingFilter.h new file mode 100644 index 000000000..8baaa1096 --- /dev/null +++ b/include/phasar/DataFlow/PathSensitivity/PathTracingFilter.h @@ -0,0 +1,51 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_DATAFLOW_PATHSENSITIVITY_PATHTRACINGFILTER_H +#define PHASAR_DATAFLOW_PATHSENSITIVITY_PATHTRACINGFILTER_H + +#include + +namespace psr { +template struct PathTracingFilter { + using end_filter_t = EndFilter; + using err_filter_t = ErrFilter; + + [[no_unique_address]] end_filter_t HasReachedEnd; + [[no_unique_address]] err_filter_t IsErrorneousTransition; +}; + +namespace detail { +struct False2 { + template + constexpr bool operator()(T && /*First*/, U && /*Second*/) const noexcept { + return false; + } +}; +} // namespace detail + +using DefaultPathTracingFilter = + PathTracingFilter; + +template +struct is_pathtracingfilter_for : std::false_type {}; + +template +struct is_pathtracingfilter_for< + PathTracingFilter, NodeRef, + std::enable_if_t && + std::is_invocable_r_v>> + : std::true_type {}; + +template +constexpr static bool is_pathtracingfilter_for_v = + is_pathtracingfilter_for::value; +} // namespace psr + +#endif // PHASAR_DATAFLOW_PATHSENSITIVITY_PATHTRACINGFILTER_H diff --git a/include/phasar/Domain.h b/include/phasar/Domain.h new file mode 100644 index 000000000..33adee8fc --- /dev/null +++ b/include/phasar/Domain.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DOMAIN_H +#define PHASAR_DOMAIN_H + +#include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Domain/LatticeDomain.h" + +#endif // PHASAR_DOMAIN_H diff --git a/include/phasar/Domain/AnalysisDomain.h b/include/phasar/Domain/AnalysisDomain.h index 02a76f4a8..bf79fe797 100644 --- a/include/phasar/Domain/AnalysisDomain.h +++ b/include/phasar/Domain/AnalysisDomain.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DOMAIN_ANALYSISDOMAIN_H -#define PHASAR_PHASARLLVM_DOMAIN_ANALYSISDOMAIN_H +#ifndef PHASAR_DOMAIN_ANALYSISDOMAIN_H +#define PHASAR_DOMAIN_ANALYSISDOMAIN_H #include diff --git a/include/phasar/Domain/BinaryDomain.h b/include/phasar/Domain/BinaryDomain.h index 80fe3d8ed..d518120f3 100644 --- a/include/phasar/Domain/BinaryDomain.h +++ b/include/phasar/Domain/BinaryDomain.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_UTILS_BINARYDOMAIN_H_ -#define PHASAR_PHASARLLVM_UTILS_BINARYDOMAIN_H_ +#ifndef PHASAR_DOMAIN_BINARYDOMAIN_H +#define PHASAR_DOMAIN_BINARYDOMAIN_H #include "phasar/Utils/JoinLattice.h" diff --git a/include/phasar/Domain/LatticeDomain.h b/include/phasar/Domain/LatticeDomain.h index 76dd845bc..b7b7f1540 100644 --- a/include/phasar/Domain/LatticeDomain.h +++ b/include/phasar/Domain/LatticeDomain.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_UTILS_LATTICEDOMAIN_H -#define PHASAR_PHASARLLVM_UTILS_LATTICEDOMAIN_H +#ifndef PHASAR_DOMAIN_LATTICEDOMAIN_H +#define PHASAR_DOMAIN_LATTICEDOMAIN_H #include "phasar/Utils/ByRef.h" #include "phasar/Utils/JoinLattice.h" diff --git a/include/phasar/PhasarClang.h b/include/phasar/PhasarClang.h new file mode 100644 index 000000000..1ff5571f6 --- /dev/null +++ b/include/phasar/PhasarClang.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2023 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_PHASARCLANG_H +#define PHASAR_PHASARCLANG_H + +#include "PhasarClang/ClangController.h" +#include "PhasarClang/RandomChangeASTConsumer.h" +#include "PhasarClang/RandomChangeFrontendAction.h" +#include "PhasarClang/RandomChangeVisitor.h" + +#endif // PHASAR_PHASARCLANG_H diff --git a/include/phasar/PhasarLLVM.h b/include/phasar/PhasarLLVM.h new file mode 100644 index 000000000..051d89822 --- /dev/null +++ b/include/phasar/PhasarLLVM.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) 2023 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_H +#define PHASAR_PHASARLLVM_H + +#include "PhasarLLVM/ControlFlow.h" +#include "PhasarLLVM/DB.h" +#include "PhasarLLVM/DataFlow.h" +#include "PhasarLLVM/Domain.h" +#include "PhasarLLVM/HelperAnalyses.h" +#include "PhasarLLVM/HelperAnalysisConfig.h" +#include "PhasarLLVM/Passes.h" +#include "PhasarLLVM/Pointer.h" +#include "PhasarLLVM/SimpleAnalysisConstructor.h" +#include "PhasarLLVM/TaintConfig.h" +#include "PhasarLLVM/TypeHierarchy.h" +#include "PhasarLLVM/Utils.h" + +#endif // PHASAR_PHASARLLVM_H diff --git a/include/phasar/PhasarLLVM/ControlFlow.h b/include/phasar/PhasarLLVM/ControlFlow.h new file mode 100644 index 000000000..0f4b30135 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_CONTROLFLOW_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_H + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.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/Resolver/Resolver.h" + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h index 10a342488..101718a42 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h @@ -19,9 +19,14 @@ namespace psr { class LLVMBasedICFG; +class LLVMBasedBackwardICFG; +template class CallGraph; + +template <> +struct CFGTraits : CFGTraits {}; class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, - public ICFGBase { + public ICFGBase { friend ICFGBase; class LLVMBackwardRet { @@ -60,6 +65,7 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; [[nodiscard]] nlohmann::json getAsJsonImpl() const; + [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; llvm::LLVMContext BackwardRetsCtx; llvm::DenseMap BackwardRets; @@ -68,6 +74,8 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, LLVMBasedICFG *ForwardICFG{}; }; + +extern template class ICFGBase; } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h index 2c7ac54cf..a3134c713 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h @@ -18,8 +18,6 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" -#include "nlohmann/json.hpp" - namespace llvm { class Function; } // namespace llvm @@ -28,6 +26,9 @@ namespace psr { class LLVMBasedCFG; class LLVMBasedBackwardCFG; +// Forward-declaration to avoid including LLVMShorthands.h here +bool isHeapAllocatingFunction(const llvm::Function *F) noexcept; + template <> struct CFGTraits { using n_t = const llvm::Instruction *; using f_t = const llvm::Function *; @@ -78,7 +79,9 @@ template class LLVMBasedCFGImpl : public CFGBase { [[nodiscard]] bool isFallThroughSuccessorImpl(n_t Inst, n_t Succ) const noexcept; [[nodiscard]] bool isBranchTargetImpl(n_t Inst, n_t Succ) const noexcept; - [[nodiscard]] bool isHeapAllocatingFunctionImpl(f_t Fun) const; + [[nodiscard]] bool isHeapAllocatingFunctionImpl(f_t Fun) const { + return psr::isHeapAllocatingFunction(Fun); + } [[nodiscard]] bool isSpecialMemberFunctionImpl(f_t Fun) const { return this->getSpecialMemberFunctionType(Fun) != SpecialMemberFunctionType{}; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h index 3878aa155..12bc78493 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h @@ -17,6 +17,7 @@ #ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDICFG_H_ #define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDICFG_H_ +#include "phasar/ControlFlow/CallGraph.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/ControlFlow/ICFGBase.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" @@ -29,6 +30,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Value.h" #include "llvm/Support/raw_ostream.h" @@ -37,16 +39,8 @@ #include -/// On some MAC systems, is still not fully implemented, so do -/// a workaround here - -#if !HAS_MEMORY_RESOURCE -#include "llvm/Support/Allocator.h" -#endif - namespace psr { class LLVMTypeHierarchy; -class LLVMPointsToInfo; class LLVMProjectIRDB; class LLVMBasedICFG; @@ -57,14 +51,19 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { struct Builder; - struct OnlyDestroyDeleter { - template void operator()(T *Data) { std::destroy_at(Data); } - }; - public: static constexpr llvm::StringLiteral GlobalCRuntimeModelName = "__psrCRuntimeGlobalCtorsModel"; + static constexpr llvm::StringLiteral GlobalCRuntimeDtorModelName = + "__psrCRuntimeGlobalDtorsModel"; + + static constexpr llvm::StringLiteral GlobalCRuntimeDtorsCallerName = + "__psrGlobalDtorsCaller"; + + static constexpr llvm::StringLiteral GlobalCRuntimeUserEntrySelectorName = + "__psrCRuntimeUserEntrySelector"; + /// Constructs the ICFG based on the given IRDB and the entry-points using a /// fixpoint iteration. This may take a long time. /// @@ -89,13 +88,22 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { Soundness S = Soundness::Soundy, bool IncludeGlobals = true); + /// Creates an ICFG with an already given call-graph + explicit LLVMBasedICFG(CallGraph CG, LLVMProjectIRDB *IRDB, + LLVMTypeHierarchy *TH = nullptr); + + explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, + const nlohmann::json &SerializedCG, + LLVMTypeHierarchy *TH = nullptr); + + // Deleter of LLVMTypeHierarchy may be unknown here... ~LLVMBasedICFG(); LLVMBasedICFG(const LLVMBasedICFG &) = delete; LLVMBasedICFG &operator=(const LLVMBasedICFG &) = delete; - LLVMBasedICFG(LLVMBasedICFG &&) noexcept = delete; - LLVMBasedICFG &operator=(LLVMBasedICFG &&) noexcept = delete; + LLVMBasedICFG(LLVMBasedICFG &&) noexcept = default; + LLVMBasedICFG &operator=(LLVMBasedICFG &&) noexcept = default; /// Exports the whole ICFG (not only the call-graph) as DOT. /// @@ -108,13 +116,22 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { [[nodiscard]] nlohmann::json exportICFGAsJson(bool WithSourceCodeInfo = true) const; + [[nodiscard]] size_t getNumVertexFunctions() const noexcept { + return CG.getNumVertexFunctions(); + } + /// Returns all functions from the underlying IRDB that are part of the ICFG, /// i.e. that are reachable from the entry-points - [[nodiscard]] llvm::ArrayRef getAllVertexFunctions() const noexcept; + [[nodiscard]] auto getAllVertexFunctions() const noexcept { + return CG.getAllVertexFunctions(); + } /// Gets the underlying IRDB [[nodiscard]] LLVMProjectIRDB *getIRDB() const noexcept { return IRDB; } + /// Returns true, if a function was generated by phasar. + [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &) noexcept; + using CFGBase::print; using ICFGBase::print; @@ -128,50 +145,26 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { [[nodiscard]] bool isIndirectFunctionCallImpl(n_t Inst) const; [[nodiscard]] bool isVirtualFunctionCallImpl(n_t Inst) const; [[nodiscard]] std::vector allNonCallStartNodesImpl() const; - [[nodiscard]] llvm::ArrayRef - getCalleesOfCallAtImpl(n_t Inst) const noexcept; - [[nodiscard]] llvm::ArrayRef getCallersOfImpl(f_t Fun) const noexcept; [[nodiscard]] llvm::SmallVector getCallsFromWithinImpl(f_t Fun) const; [[nodiscard]] llvm::SmallVector getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; [[nodiscard]] nlohmann::json getAsJsonImpl() const; + [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept { + return CG; + } [[nodiscard]] llvm::Function *buildCRuntimeGlobalCtorsDtorsModel( llvm::Module &M, llvm::ArrayRef UserEntryPoints); - // -------------------- Utilities -------------------- - - llvm::SmallVector * - addFunctionVertex(const llvm::Function *F); - llvm::SmallVector * - addInstructionVertex(const llvm::Instruction *Inst); - - void addCallEdge(const llvm::Instruction *CS, const llvm::Function *Callee); - void addCallEdge(const llvm::Instruction *CS, - llvm::SmallVector *Callees, - const llvm::Function *Callee); - -#if HAS_MEMORY_RESOURCE - std::pmr::monotonic_buffer_resource MRes; -#else - llvm::BumpPtrAllocator MRes; -#endif - - llvm::DenseMap, - OnlyDestroyDeleter>> - CalleesAt; - llvm::DenseMap, - OnlyDestroyDeleter>> - CallersOf; - - llvm::SmallVector VertexFunctions; + // --- + CallGraph CG; LLVMProjectIRDB *IRDB = nullptr; MaybeUniquePtr TH; }; + +extern template class ICFGBase; } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DB.h b/include/phasar/PhasarLLVM/DB.h new file mode 100644 index 000000000..e164ece2f --- /dev/null +++ b/include/phasar/PhasarLLVM/DB.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_DB_H +#define PHASAR_PHASARLLVM_DB_H + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" + +#endif // PHASAR_PHASARLLVM_DB_H diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index d1b1789d2..47df476ad 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_DB_LLVMPROJECTIRDB_H -#define PHASAR_DB_LLVMPROJECTIRDB_H +#ifndef PHASAR_PHASARLLVM_DB_LLVMPROJECTIRDB_H +#define PHASAR_PHASARLLVM_DB_LLVMPROJECTIRDB_H #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" @@ -22,6 +22,7 @@ #include "llvm/IR/Instruction.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/Support/MemoryBufferRef.h" #include "llvm/Support/raw_ostream.h" #include @@ -40,18 +41,24 @@ class LLVMProjectIRDB : public ProjectIRDBBase { friend ProjectIRDBBase; public: - /// Reads and parses the given LLVM IR file and owns the resulting IR Module + /// Reads and parses the given LLVM IR file and owns the resulting IR Module. + /// If an error occurs, an error message is written to stderr and subsequent + /// calls to isValid() return false. explicit LLVMProjectIRDB(const llvm::Twine &IRFileName); /// Initializes the new ProjectIRDB with the given IR Module _without_ taking - /// ownership. The module is not being preprocessed. + /// ownership. The module is optionally being preprocessed. /// /// CAUTION: Do not manage the same LLVM Module with multiple LLVMProjectIRDB /// instances at the same time! This will confuse the ModulesToSlotTracker - explicit LLVMProjectIRDB(llvm::Module *Mod); - /// Initializes the new ProjectIRDB with the given IR Moduleand takes - /// ownership of it + explicit LLVMProjectIRDB(llvm::Module *Mod, bool DoPreprocessing = true); + /// Initializes the new ProjectIRDB with the given IR Module and takes + /// ownership of it. The module is optionally being preprocessed. explicit LLVMProjectIRDB(std::unique_ptr Mod, bool DoPreprocessing = true); + /// Parses the given LLVM IR file and owns the resulting IR Module. + /// If an error occurs, an error message is written to stderr and subsequent + /// calls to isValid() return false. + explicit LLVMProjectIRDB(llvm::MemoryBufferRef Buf); LLVMProjectIRDB(const LLVMProjectIRDB &) = delete; LLVMProjectIRDB &operator=(LLVMProjectIRDB &) = delete; @@ -61,6 +68,9 @@ class LLVMProjectIRDB : public ProjectIRDBBase { [[nodiscard]] static std::unique_ptr getParsedIRModuleOrNull(const llvm::Twine &IRFileName, llvm::LLVMContext &Ctx) noexcept; + [[nodiscard]] static std::unique_ptr + getParsedIRModuleOrNull(llvm::MemoryBufferRef IRFileContent, + llvm::LLVMContext &Ctx) noexcept; /// Also use the const overload using ProjectIRDBBase::getFunction; @@ -78,7 +88,7 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// Also use the const overload using ProjectIRDBBase::getModule; /// Non-const overload - [[nodiscard]] llvm::Module *getModule() { return Mod.get(); } + [[nodiscard]] llvm::Module *getModule() noexcept { return Mod.get(); } /// Similar to getInstruction(size_t), but is also able to return global /// variables by id @@ -93,11 +103,13 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// called twice for the same function. Use with care! void insertFunction(llvm::Function *F, bool DoPreprocessing = true); + explicit operator bool() const noexcept { return isValid(); } + private: [[nodiscard]] m_t getModuleImpl() const noexcept { return Mod.get(); } [[nodiscard]] bool debugInfoAvailableImpl() const; [[nodiscard]] FunctionRange getAllFunctionsImpl() const { - return llvm::map_range(Mod->functions(), + return llvm::map_range(ProjectIRDBBase::getModule()->functions(), Ref2PointerConverter{}); } [[nodiscard]] f_t getFunctionImpl(llvm::StringRef FunctionName) const { @@ -165,4 +177,4 @@ const llvm::Value *fromMetaDataId(const LLVMProjectIRDB &IRDB, extern template class ProjectIRDBBase; } // namespace psr -#endif // PHASAR_DB_LLVMPROJECTIRDB_H +#endif // PHASAR_PHASARLLVM_DB_LLVMPROJECTIRDB_H diff --git a/include/phasar/PhasarLLVM/DataFlow.h b/include/phasar/PhasarLLVM/DataFlow.h new file mode 100644 index 000000000..abae6a208 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_DATAFLOW_H +#define PHASAR_PHASARLLVM_DATAFLOW_H + +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.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/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/IntraMonoFullConstantPropagation.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h" +#include "phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h" + +#endif // PHASAR_PHASARLLVM_DATAFLOW_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h index 75129176f..016030332 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_BRANCHSWITCHINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_BRANCHSWITCHINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_BRANCHSWITCHINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_BRANCHSWITCHINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h index efa2f22b9..01aa4b9b4 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CALLTORETFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CALLTORETFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CALLTORETFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CALLTORETFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h index 2a9f2f5bf..a89c93ad1 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CHECKOPERANDSFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CHECKOPERANDSFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CHECKOPERANDSFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_CHECKOPERANDSFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h index 0117e364b..8e1e0db4a 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_FLOWFUNCTIONBASE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_FLOWFUNCTIONBASE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_FLOWFUNCTIONBASE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_FLOWFUNCTIONBASE_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde//IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h index 4ebf27b65..0f382d1e4 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GEPINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GEPINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GEPINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GEPINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h index 969e7dd73..e6056f156 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GENERATEFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GENERATEFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GENERATEFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_GENERATEFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h index 0aa541206..9f410fd2c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_IDENTITYFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_IDENTITYFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_IDENTITYFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_IDENTITYFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h index fcca65bf6..245108ce7 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLEE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLEE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLEE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLEE_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde//IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h index 57477a0ad..050cef273 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MAPTAINTEDVALUESTOCALLER_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde//IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h index 9c12e828b..c0e7ec30b 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMSETINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMSETINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMSETINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMSETINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h index 7d52a7458..d54375155 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMTRANSFERINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMTRANSFERINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMTRANSFERINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_MEMTRANSFERINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h index 1aa55b8eb..c5df55522 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_PHINODEFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_PHINODEFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_PHINODEFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_PHINODEFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h index 700699152..88785ef91 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_RETURNINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_RETURNINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_RETURNINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_RETURNINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h index c2e121de1..4ffc14e9d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_STOREINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_STOREINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_STOREINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_STOREINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h index 04949dae2..270732856 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VAENDINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VAENDINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VAENDINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VAENDINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h index 3b7980bc5..b59b67ae2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VASTARTINSTFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VASTARTINSTFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VASTARTINSTFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_FLOWFUNCTIONS_VASTARTINSTFLOWFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h index 0c5664800..a56be6137 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H #include "TraceStatsWriter.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h index d7e27b295..4b6690102 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H #include "TraceStatsWriter.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h index 72a927b63..763fc1879 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H #include diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h index 23fe53f94..d92c5efe3 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H #include "TraceStatsWriter.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h index 5ba0fcd25..2bdb6d59a 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H #include "llvm/IR/Instruction.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h index d4d5f599d..5ec4eb8ee 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATSWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATSWRITER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATSWRITER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATSWRITER_H #include "../Utils/Log.h" #include "TraceStats.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h index cb936e237..3fa5c74e9 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_DATAFLOWUTILS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_DATAFLOWUTILS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_DATAFLOWUTILS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_DATAFLOWUTILS_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h index 45139ba97..01ff03246 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h @@ -2,8 +2,10 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DOMAIN_EXTENDEDVALUE_H -#define PHASAR_PHASARLLVM_DOMAIN_EXTENDEDVALUE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_EXTENDEDVALUE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_EXTENDEDVALUE_H + +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include #include diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h index 24ab01522..29b3835b3 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H #include "llvm/Support/raw_ostream.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h index ef3dd2c83..e994671bb 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_LLVMFLOWFUNCTIONS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_LLVMFLOWFUNCTIONS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFLOWFUNCTIONS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMFLOWFUNCTIONS_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" @@ -179,6 +179,12 @@ mapFactsToCallee(const llvm::CallBase *CallSite, const llvm::Function *DestFun, llvm::Function::const_arg_iterator ParamIt = DestFun->arg_begin(); llvm::Function::const_arg_iterator ParamEnd = DestFun->arg_end(); + if (ParamIt != ParamEnd && (*ParamIt).hasStructRetAttr()) { + // sret parameters are writeonly + ++ParamIt; + ++ArgIt; + } + for (; ParamIt != ParamEnd; ++ParamIt, ++ArgIt) { if (std::invoke(PropArg, ArgIt->get(), Source)) { Res.insert(std::invoke(FactConstructor, &*ParamIt)); @@ -233,7 +239,8 @@ mapFactsToCallee(const llvm::CallBase *CallSite, const llvm::Function *DestFun, /// /// Propagates the return value back to the call-site and based on the /// PropagateParameter predicate propagates back parameters holding as dataflow -/// facts. +/// facts. The resulting out-set of dataflow facts can be post-processed if +/// necessary. /// /// Let a call-site cs: r = fun(..., ax, ...) a function prototype fun(..., /// px, ...) and an exit statement exit: return rv. @@ -246,30 +253,30 @@ mapFactsToCallee(const llvm::CallBase *CallSite, const llvm::Function *DestFun, /// f(x) = ({ax} if PropagateParameter(ax, x) else {}) union ({r} if /// PropagateRet(rv, x) else {}). /// -template , - typename FnParam = std::equal_to, - typename FnRet = std::equal_to, - typename DCtor = DefaultConstruct, - typename = std::enable_if_t< - std::is_invocable_r_v && - std::is_invocable_r_v>> -FlowFunctionPtrType -mapFactsToCaller(const llvm::CallBase *CallSite, - const llvm::Instruction *ExitInst, - FnParam &&PropagateParameter = {}, FnRet &&PropagateRet = {}, - DCtor &&FactConstructor = {}, bool PropagateGlobals = true, - bool PropagateZeroToCaller = true) { +template < + typename D = const llvm::Value *, typename Container = std::set, + typename FnParam = std::equal_to, typename FnRet = std::equal_to, + typename DCtor = DefaultConstruct, typename PostProcessFn = IgnoreArgs, + typename = std::enable_if_t< + std::is_invocable_r_v && + std::is_invocable_r_v>> +FlowFunctionPtrType mapFactsToCaller( + const llvm::CallBase *CallSite, const llvm::Instruction *ExitInst, + FnParam &&PropagateParameter = {}, FnRet &&PropagateRet = {}, + DCtor &&FactConstructor = {}, bool PropagateGlobals = true, + bool PropagateZeroToCaller = true, PostProcessFn &&PostProcess = {}) { struct Mapper : public FlowFunction { Mapper(const llvm::CallBase *CallSite, const llvm::Instruction *ExitInst, bool PropagateGlobals, FnParam &&PropagateParameter, FnRet &&PropagateRet, DCtor &&FactConstructor, - bool PropagateZeroToCaller) + bool PropagateZeroToCaller, PostProcessFn &&PostProcess) : CSAndPropGlob(CallSite, PropagateGlobals), ExitInstAndPropZero(ExitInst, PropagateZeroToCaller), PropArg(std::forward(PropagateParameter)), PropRet(std::forward(PropagateRet)), - FactConstructor(std::forward(FactConstructor)) {} + FactConstructor(std::forward(FactConstructor)), + PostProcess(std::forward(PostProcess)) {} Container computeTargets(D Source) override { Container Res; @@ -331,6 +338,8 @@ mapFactsToCaller(const llvm::CallBase *CallSite, } } + std::invoke(PostProcess, Res); + return Res; } @@ -340,34 +349,14 @@ mapFactsToCaller(const llvm::CallBase *CallSite, [[no_unique_address]] std::decay_t PropArg; [[no_unique_address]] std::decay_t PropRet; [[no_unique_address]] std::decay_t FactConstructor; + [[no_unique_address]] std::decay_t PostProcess; }; - return std::make_shared(CallSite, ExitInst, PropagateGlobals, - std::forward(PropagateParameter), - std::forward(PropagateRet), - std::forward(FactConstructor), - PropagateZeroToCaller); -} - -//===----------------------------------------------------------------------===// -// Propagation flow functions - -/// Utility function to simplify writing a flow function of the form: -/// generateFlow(Load, from: Load->getPointerOperand()). -template > -FlowFunctionPtrType -propagateLoad(const llvm::LoadInst *Load) { - return generateFlow( - Load, Load->getPointerOperand()); -} - -/// Utility function to simplify writing a flow function of the form: -/// generateFlow(Store->getValueOperand(), from: Store->getPointerOperand()). -template > -FlowFunctionPtrType -propagateStore(const llvm::StoreInst *Store) { - return generateFlow( - Store->getValueOperand(), Store->getPointerOperand()); + return std::make_shared( + CallSite, ExitInst, PropagateGlobals, + std::forward(PropagateParameter), + std::forward(PropagateRet), std::forward(FactConstructor), + PropagateZeroToCaller, std::forward(PostProcess)); } //===----------------------------------------------------------------------===// diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h new file mode 100644 index 000000000..4d17597b7 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * 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, Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMSOLVERRESULTS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMSOLVERRESULTS_H + +#include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Logger.h" + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/Support/ErrorHandling.h" + +namespace psr::detail { + +/// FIXME: This is not entirely correct: Does not skip ignored statements and +/// does not work for backwards analyses. +/// The right way would be to ask the ICFG, but we don't have a reference to it +/// here yet (TODO!) +template +template +auto SolverResultsBase::resultsAtInLLVMSSA( + ByConstRef Stmt, bool AllowOverapproximation, bool StripZero) const -> + typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + std::unordered_map> { + std::unordered_map Result = [this, Stmt, AllowOverapproximation]() { + if (Stmt->getType()->isVoidTy()) { + return self().Results.row(Stmt); + } + if (!Stmt->getNextNode()) { + auto GetStartRow = [this](const llvm::BasicBlock *BB) -> decltype(auto) { + const auto *First = &BB->front(); + if (llvm::isa(First)) { + First = First->getNextNonDebugInstruction(); + } + return self().Results.row(First); + }; + + // We have reached the end of a BasicBlock. If there is a successor BB + // that only has one predecessor, we are lucky and can just take results + // from there + for (const llvm::BasicBlock *Succ : llvm::successors(Stmt)) { + if (Succ->hasNPredecessors(1)) { + return GetStartRow(Succ); + } + } + + if (!AllowOverapproximation) { + llvm::report_fatal_error("[resultsAtInLLVMSSA]: Cannot precisely " + "collect the results at instruction " + + llvm::Twine(llvmIRToString(Stmt))); + } + + // There is no successor with only one predecessor. + // All we can do is merge the results from all successors to get a sound + // overapproximation. This is not optimal and may be replaced in the + // future. + PHASAR_LOG_LEVEL(WARNING, "[resultsAtInLLVMSSA]: Cannot precisely " + "collect the results at instruction " + << llvmIRToString(Stmt) + << ". Use a sound, but potentially " + "imprecise overapproximation"); + std::unordered_map Ret; + for (const llvm::BasicBlock *Succ : llvm::successors(Stmt)) { + const auto &Row = GetStartRow(Succ); + for (const auto &[Fact, Value] : Row) { + auto [It, Inserted] = Ret.try_emplace(Fact, Value); + if (!Inserted && Value != It->second) { + if constexpr (HasJoinLatticeTraits) { + It->second = JoinLatticeTraits::join(It->second, Value); + } else { + // We have no way of correctly merging, so set the value to the + // default constructed l_t hoping it marks BOTTOM. + It->second = l_t(); + } + } + } + } + return Ret; + } + assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); + return self().Results.row(Stmt->getNextNode()); + }(); + if (StripZero) { + Result.erase(self().ZV); + } + return Result; +} + +template +template +auto SolverResultsBase::resultAtInLLVMSSA( + ByConstRef Stmt, d_t Value, bool AllowOverapproximation) const -> + typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + l_t> { + if (Stmt->getType()->isVoidTy()) { + return self().Results.get(Stmt, Value); + } + if (!Stmt->getNextNode()) { + auto GetStartVal = [this, + &Value](const llvm::BasicBlock *BB) -> decltype(auto) { + const auto *First = &BB->front(); + if (llvm::isa(First)) { + First = First->getNextNonDebugInstruction(); + } + return self().Results.get(First, Value); + }; + + // We have reached the end of a BasicBlock. If there is a successor BB + // that only has one predecessor, we are lucky and can just take results + // from there + for (const llvm::BasicBlock *Succ : llvm::successors(Stmt)) { + if (Succ->hasNPredecessors(1)) { + return GetStartVal(Succ); + } + } + + if (!AllowOverapproximation) { + llvm::report_fatal_error("[resultsAtInLLVMSSA]: Cannot precisely " + "collect the results at instruction " + + llvm::Twine(llvmIRToString(Stmt))); + } + + // There is no successor with only one predecessor. + // All we can do is merge the results from all successors to get a sound + // overapproximation. This is not optimal and may be replaced in the + // future. + PHASAR_LOG_LEVEL(WARNING, "[resultAtInLLVMSSA]: Cannot precisely " + "collect the results at instruction " + << *Stmt + << ". Use a sound, but potentially " + "imprecise overapproximation"); + auto It = llvm::succ_begin(Stmt); + auto End = llvm::succ_end(Stmt); + l_t Ret{}; + if (It != End) { + Ret = GetStartVal(*It); + for (++It; It != End; ++It) { + const auto &Val = GetStartVal(*It); + if constexpr (HasJoinLatticeTraits) { + Ret = JoinLatticeTraits::join(Ret, Val); + if (Ret == JoinLatticeTraits::bottom()) { + break; + } + } else { + if (Ret != Val) { + // We have no way of correctly merging, so set the value to the + // default constructed l_t hoping it marks BOTTOM. + return {}; + } + } + } + } + return Ret; + } + assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); + return self().Results.get(Stmt->getNextNode(), Value); +} +} // namespace psr::detail + +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMSOLVERRESULTS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h index c57bfd088..ad55a9840 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_LLVMZEROVALUE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_LLVMZEROVALUE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMZEROVALUE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMZEROVALUE_H #include "llvm/ADT/StringRef.h" #include "llvm/IR/GlobalVariable.h" @@ -53,7 +53,7 @@ class LLVMZeroValue : public llvm::GlobalVariable { static const LLVMZeroValue *getInstance(); // NOLINTNEXTLINE(readability-identifier-naming) - static constexpr auto isLLVMZeroValue = [](const llvm::Value *V) { + static constexpr auto isLLVMZeroValue = [](const llvm::Value *V) noexcept { return V == getInstance(); }; }; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h index 5f7dbf727..365036042 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h index aa27ef024..a492bdd6a 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocationFactory.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATIONFACTORY_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATIONFACTORY_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATIONFACTORY_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ABSTRACTMEMORYLOCATIONFACTORY_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AbstractMemoryLocation.h" @@ -41,7 +41,7 @@ class AbstractMemoryLocationFactoryBase { Block *Next = nullptr; static Block *create(Block *Next, size_t NumPointerEntries); - static void destroy(Block *Blck); + static void destroy(Block *Blck, size_t NumPointerEntries); private: Block(Block *Next); @@ -49,6 +49,7 @@ class AbstractMemoryLocationFactoryBase { Block *Root = nullptr; void **Pos = nullptr, **End = nullptr; + size_t InitialCapacity{}; Allocator() noexcept = default; Allocator(size_t InitialCapacity); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h index 2288416f4..0605a9601 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h" @@ -32,4 +32,4 @@ struct AllSanitized { }; } // namespace psr::XTaint -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_ALLSANITIZED_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/ComposeEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/ComposeEdgeFunction.h index 981461c48..debf1bf3f 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/ComposeEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/ComposeEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_COMPOSEEDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_COMPOSEEDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_COMPOSEEDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_COMPOSEEDGEFUNCTION_H #include "phasar//DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h index ad3d9404e..c246c5721 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_EDGEDOMAIN_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_EDGEDOMAIN_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_EDGEDOMAIN_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_EDGEDOMAIN_H #include "phasar/Domain/LatticeDomain.h" #include "phasar/Utils/ByRef.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h index 4376f40eb..98bcc97b4 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/GenEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_GENEDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_GENEDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_GENEDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_GENEDGEFUNCTION_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/EdgeDomain.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h index a4bd479ca..862af3e47 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_HELPERS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_HELPERS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_HELPERS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_HELPERS_H #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/Domain/LatticeDomain.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/KillIfSanitizedEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/KillIfSanitizedEdgeFunction.h index 181a9190f..680f0c206 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/KillIfSanitizedEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/KillIfSanitizedEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_KILLIFSANITIZEDEDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_KILLIFSANITIZEDEDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_KILLIFSANITIZEDEDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_KILLIFSANITIZEDEDGEFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h" #include "phasar/Utils/ByRef.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h index 450a8519e..a5383208d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/TransferEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_TRANSFEREDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_TRANSFEREDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_TRANSFEREDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_TRANSFEREDGEFUNCTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h" #include "phasar/Utils/ByRef.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h index 5263343fb..eda4bd6e4 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintAnalysisBase.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTANALYSISBASE_H #include "llvm/ADT/SmallPtrSet.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h index 339fa2f81..edd529101 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/XTaintEdgeFunctionBase.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTEDGEFUNCTIONBASE_H_ -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTEDGEFUNCTIONBASE_H_ +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTEDGEFUNCTIONBASE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_EXTENDEDTAINTANALYSIS_XTAINTEDGEFUNCTIONBASE_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/AllSanitized.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h index cb13c7344..e7dceb8eb 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEEXTENDEDTAINTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/Domain/LatticeDomain.h" @@ -246,28 +246,10 @@ class IDEExtendedTaintAnalysis [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - EdgeFunctionType allTopFunction() override; - - // JoinLattice - - l_t topElement() override; - - l_t bottomElement() override; - - l_t join(l_t LHS, l_t RHS) override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; // Printing functions - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; - void emitTextReport(const SolverResults &SR, llvm::raw_ostream &OS = llvm::outs()) override; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h index 0dd0789a4..fa0984801 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_ALLBOT_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_ALLBOT_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_ALLBOT_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_ALLBOT_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h index d65f87f55..f24453bd1 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/BinaryEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_BINARYEDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_BINARYEDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_BINARYEDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_BINARYEDGEFUNCTION_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCADomain.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/ConstantHelper.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/ConstantHelper.h index 34c7ffa45..cceb17979 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/ConstantHelper.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/ConstantHelper.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_CONSTANTHELPER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_CONSTANTHELPER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_CONSTANTHELPER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_CONSTANTHELPER_H namespace llvm { class Value; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h index 1fa752cb9..c26a6191c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h @@ -7,8 +7,8 @@ * Fabian Schiebel, Alexander Meinhold and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H #include "llvm/ADT/APSInt.h" #include "llvm/ADT/Twine.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h index 8526c3474..0b8b859bf 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUESET_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUESET_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUESET_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUESET_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h" #include "phasar/Utils/JoinLattice.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/GenConstant.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/GenConstant.h index 50b14da39..4a4246efc 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/GenConstant.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/GenConstant.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_GENCONSTANT_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_GENCONSTANT_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_GENCONSTANT_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_GENCONSTANT_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h index 63949390d..edde5b462 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCA_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCA_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCA_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCA_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" @@ -74,7 +74,7 @@ class IDEGeneralizedLCA : public IDETabulationProblem { [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; // in addition provide specifications for the IDE parts @@ -106,14 +106,6 @@ class IDEGeneralizedLCA : public IDETabulationProblem { EdgeFunction allTopFunction() override; - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; - // void printIDEReport(llvm::raw_ostream &OS, // SolverResults &SR) override; void emitTextReport(const SolverResults &SR, @@ -127,7 +119,6 @@ class IDEGeneralizedLCA : public IDETabulationProblem { void stripBottomResults(std::unordered_map &Res); [[nodiscard]] bool isEntryPoint(const std::string &Name) const; - template std::string VtoString(V Val); // NOLINT bool isStringConstructor(const llvm::Function *Func); }; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCADomain.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCADomain.h index c4b07728a..fc387e013 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCADomain.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCADomain.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -23,4 +23,4 @@ struct IDEGeneralizedLCADomain : LLVMAnalysisDomainDefault { } // namespace psr -#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_IDEGENERALIZEDLCADOMAIN_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/LCAEdgeFunctionComposer.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/LCAEdgeFunctionComposer.h index a0bac57af..e345b9472 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/LCAEdgeFunctionComposer.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/LCAEdgeFunctionComposer.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_LCAEDGEFUNCTIONCOMPOSER_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_LCAEDGEFUNCTIONCOMPOSER_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_LCAEDGEFUNCTIONCOMPOSER_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_LCAEDGEFUNCTIONCOMPOSER_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCalleeFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCalleeFlowFunction.h index f6a11eb95..99f580244 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCalleeFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCalleeFlowFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLEEFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLEEFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLEEFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLEEFLOWFUNCTION_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCallerFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCallerFlowFunction.h index 50acbe5c6..b2fc9f691 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCallerFlowFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/MapFactsToCallerFlowFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLERFLOWFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLERFLOWFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLERFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_MAPFACTSTOCALLERFLOWFUNCTION_H #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/TypecastEdgeFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/TypecastEdgeFunction.h index 426b4d0b1..c910e2e21 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/TypecastEdgeFunction.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/TypecastEdgeFunction.h @@ -7,8 +7,8 @@ * Fabian Schiebel and Others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_TYPECASTEDGEFUNCTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_TYPECASTEDGEFUNCTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_TYPECASTEDGEFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_TYPECASTEDGEFUNCTION_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValueSet.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h index c39808640..40d1dff44 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_IFDSIDE_PROBLEMS_IDEINSTINTERACTIONALYSIS_H -#define PHASAR_PHASARLLVM_IFDSIDE_PROBLEMS_IDEINSTINTERACTIONALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEINSTINTERACTIONANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEINSTINTERACTIONANALYSIS_H #include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" @@ -18,6 +18,7 @@ #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" @@ -29,6 +30,7 @@ #include "phasar/Utils/Logger.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Constant.h" @@ -269,7 +271,7 @@ class IDEInstInteractionAnalysisT // if (const auto *Alloca = llvm::dyn_cast(Curr)) { PHASAR_LOG_LEVEL(DFADEBUG, "AllocaInst"); - return generateFromZero(Alloca); + return this->generateFromZero(Alloca); } // Handle indirect taints, i. e., propagate values that depend on branch @@ -292,7 +294,7 @@ class IDEInstInteractionAnalysisT // v v v v // 0 c x I // - return lambdaFlow([Br](d_t Src) { + return this->lambdaFlow([Br](d_t Src) { container_type Facts; Facts.insert(Src); if (Src == Br->getCondition()) { @@ -328,7 +330,7 @@ class IDEInstInteractionAnalysisT // v v v // 0 Y x // - return generateFlowIf( + return this->generateFlowIf( Load, [PointerOp = Load->getPointerOperand(), PTS = PT.getReachableAllocationSites( Load->getPointerOperand(), OnlyConsiderLocalAliases)]( @@ -354,7 +356,7 @@ class IDEInstInteractionAnalysisT // v v v // 0 x Y // - return lambdaFlow( + return this->lambdaFlow( [Store, PointerPTS = PT.getReachableAllocationSites( Store->getPointerOperand(), OnlyConsiderLocalAliases, Store)](d_t Src) -> container_type { @@ -395,7 +397,7 @@ class IDEInstInteractionAnalysisT // 0 y x // if (const auto *Load = llvm::dyn_cast(Curr)) { - return generateFlow(Load, Load->getPointerOperand()); + return this->generateFlow(Load, Load->getPointerOperand()); } // Handle store instructions // @@ -408,7 +410,7 @@ class IDEInstInteractionAnalysisT // 0 x y // if (const auto *Store = llvm::dyn_cast(Curr)) { - return lambdaFlow([Store](d_t Src) -> container_type { + return this->lambdaFlow([Store](d_t Src) -> container_type { // Override old value, i.e., kill value that is written to and // generate from value that is stored. if (Store->getPointerOperand() == Src) { @@ -447,7 +449,7 @@ class IDEInstInteractionAnalysisT // v v v v // 0 x o p // - return lambdaFlow([Inst = Curr](d_t Src) { + return this->lambdaFlow([Inst = Curr](d_t Src) { container_type Facts; if (IDEInstInteractionAnalysisT::isZeroValueImpl(Src)) { // keep the zero flow fact @@ -482,11 +484,11 @@ class IDEInstInteractionAnalysisT f_t DestFun) override { if (this->ICF->isHeapAllocatingFunction(DestFun)) { // Kill add facts and model the effects in getCallToRetFlowFunction(). - return killAllFlows(); + return this->killAllFlows(); } if (DestFun->isDeclaration()) { // We don't have anything that we could analyze, kill all facts. - return killAllFlows(); + return this->killAllFlows(); } const auto *CS = llvm::cast(CallSite); @@ -508,9 +510,9 @@ class IDEInstInteractionAnalysisT auto SRetFormal = CS->hasStructRetAttr() ? DestFun->getArg(0) : nullptr; if (SRetFormal) { - return unionFlows( + return this->unionFlows( std::move(MapFactsToCalleeFF), - generateFlowAndKillAllOthers(SRetFormal, this->getZeroValue())); + this->generateFlowAndKillAllOthers(SRetFormal, this->getZeroValue())); } return MapFactsToCalleeFF; @@ -521,7 +523,9 @@ class IDEInstInteractionAnalysisT n_t /* RetSite */) override { // Map return value back to the caller. If pointer parameters hold at the // end of a callee function generate all of those in the caller context. - + if (CallSite == nullptr) { + return this->killAllFlows(); + } auto MapFactsToCallerFF = mapFactsToCaller(llvm::cast(CallSite), ExitInst, {}, [](const llvm::Value *RetVal, d_t Src) { @@ -581,9 +585,9 @@ class IDEInstInteractionAnalysisT } } - return lambdaFlow([CallSite = llvm::cast(CallSite), - OnlyDecls, - AllVoidRetTys](d_t Source) -> container_type { + return this->lambdaFlow([CallSite = llvm::cast(CallSite), + OnlyDecls, + AllVoidRetTys](d_t Source) -> container_type { // There are a few things to consider, in case only declarations of // callee targets are available. if (OnlyDecls) { @@ -626,26 +630,19 @@ class IDEInstInteractionAnalysisT // variables using generalized initial seeds // Generate zero value at the entry points - Seeds.addSeed(SP, this->getZeroValue(), bottomElement()); + Seeds.addSeed(SP, this->getZeroValue(), Bottom{}); // Generate formal parameters of entry points, e.g. main(). Formal // parameters will otherwise cause trouble by overriding alloca // instructions without being valid data-flow facts themselves. for (const auto &Arg : SP->getFunction()->args()) { - Seeds.addSeed(SP, &Arg, Bottom{}); + Seeds.addSeed(SP, &Arg, BitVectorSet()); } // Generate all global variables using generalized initial seeds for (const auto &G : this->IRDB->getModule()->globals()) { if (const auto *GV = llvm::dyn_cast(&G)) { - l_t InitialValues = BitVectorSet(); - std::set EdgeFacts; - if (EdgeFactGen) { - EdgeFacts = EdgeFactGen(GV); - // fill BitVectorSet - InitialValues = - BitVectorSet(EdgeFacts.begin(), EdgeFacts.end()); - } - Seeds.addSeed(SP, GV, InitialValues); + l_t InitialValues = bvSetFrom(invoke_or_default(EdgeFactGen, GV)); + Seeds.addSeed(SP, GV, std::move(InitialValues)); } } }); @@ -658,7 +655,9 @@ class IDEInstInteractionAnalysisT return LLVMZeroValue::getInstance(); } - inline bool isZeroValue(d_t d) const override { return isZeroValueImpl(d); } + inline bool isZeroValue(d_t d) const noexcept override { + return isZeroValueImpl(d); + } // In addition provide specifications for the IDE parts. @@ -686,7 +685,12 @@ class IDEInstInteractionAnalysisT if (SuccNode == Store->getPointerOperand() || PT.isInReachableAllocationSites(Store->getPointerOperand(), SuccNode, true, Store)) { - return IIAAAddLabelsEFCache.createEdgeFunction(UserEdgeFacts); + if (isZeroValue(CurrNode)) { + return IIAAKillOrReplaceEFCache.createEdgeFunction( + std::move(UserEdgeFacts)); + } + return IIAAAddLabelsEFCache.createEdgeFunction( + std::move(UserEdgeFacts)); } } @@ -704,7 +708,7 @@ class IDEInstInteractionAnalysisT // v // y // - if ((CurrNode == SuccNode) && CurrNode == Store->getPointerOperand()) { + if (CurrNode == SuccNode && CurrNode == Store->getPointerOperand()) { // y obtains its value(s) from its original allocation and the store // instruction under analysis. IF_LOG_ENABLED({ @@ -717,7 +721,8 @@ class IDEInstInteractionAnalysisT PHASAR_LOG_LEVEL(DFADEBUG, '\n'); }); // obtain label from the original allocation - return IIAAKillOrReplaceEFCache.createEdgeFunction(UserEdgeFacts); + return IIAAKillOrReplaceEFCache.createEdgeFunction( + std::move(UserEdgeFacts)); } } else { @@ -795,7 +800,11 @@ class IDEInstInteractionAnalysisT // We generate Curr in this instruction, so we have to annotate it with // edge labels - return IIAAAddLabelsEFCache.createEdgeFunction(UserEdgeFacts); + if (isZeroValue(CurrNode)) { + return IIAAKillOrReplaceEFCache.createEdgeFunction( + std::move(UserEdgeFacts)); + } + return IIAAAddLabelsEFCache.createEdgeFunction(std::move(UserEdgeFacts)); } // Otherwise stick to identity. @@ -830,7 +839,7 @@ class IDEInstInteractionAnalysisT } } if (isZeroValue(SrcNode) && SRetParams.count(DestNode)) { - return IIAAAddLabelsEFCache.createEdgeFunction(); + return IIAAKillOrReplaceEFCache.createEdgeFunction(); } // Everything else can be passed as identity. return EdgeIdentity{}; @@ -858,14 +867,8 @@ class IDEInstInteractionAnalysisT if (const auto *CD = llvm::dyn_cast(Ret->getReturnValue())) { // Check if the user has registered a fact generator function - l_t UserEdgeFacts = BitVectorSet(); - std::set EdgeFacts; - if (EdgeFactGen) { - EdgeFacts = EdgeFactGen(ExitInst); - // fill BitVectorSet - UserEdgeFacts = BitVectorSet(EdgeFacts.begin(), EdgeFacts.end()); - } - return IIAAAddLabelsEFCache.createEdgeFunction( + l_t UserEdgeFacts = bvSetFrom(invoke_or_default(EdgeFactGen, ExitInst)); + return IIAAKillOrReplaceEFCache.createEdgeFunction( std::move(UserEdgeFacts)); } } @@ -878,34 +881,29 @@ class IDEInstInteractionAnalysisT d_t RetSiteNode, llvm::ArrayRef Callees) override { // Check if the user has registered a fact generator function - l_t UserEdgeFacts = BitVectorSet(); - std::set EdgeFacts; - if (EdgeFactGen) { - EdgeFacts = EdgeFactGen(CallSite); - // fill BitVectorSet - UserEdgeFacts = BitVectorSet(EdgeFacts.begin(), EdgeFacts.end()); - } + l_t UserEdgeFacts = bvSetFrom(invoke_or_default(EdgeFactGen, CallSite)); + // Model call to heap allocating functions (new, new[], malloc, etc.) -- // only model direct calls, though. if (Callees.size() == 1) { - for (const auto *Callee : Callees) { - if (this->ICF->isHeapAllocatingFunction(Callee)) { - // Let H be a heap allocating function. - // - // 0 --> x - // - // Edge function: - // - // 0 - // \ + const auto *Callee = Callees.front(); + + if (this->ICF->isHeapAllocatingFunction(Callee)) { + // Let H be a heap allocating function. + // + // 0 --> x + // + // Edge function: + // + // 0 + // \ // %i = call H \ \x.x \cup { commit of('%i = call H') } - // v - // i - // - if (isZeroValue(CallNode) && RetSiteNode == CallSite) { - return IIAAAddLabelsEFCache.createEdgeFunction( - std::move(UserEdgeFacts)); - } + // v + // i + // + if (isZeroValue(CallNode) && RetSiteNode == CallSite) { + return IIAAKillOrReplaceEFCache.createEdgeFunction( + std::move(UserEdgeFacts)); } } } @@ -939,21 +937,15 @@ class IDEInstInteractionAnalysisT return nullptr; } - inline l_t topElement() override { return Top{}; } - - inline l_t bottomElement() override { return Bottom{}; } - inline l_t join(l_t Lhs, l_t Rhs) override { return joinImpl(Lhs, Rhs); } - inline EdgeFunctionType allTopFunction() override { return AllTop(); } - // Provide some handy helper edge functions to improve reuse. // Edge function that kills all labels in a set (and may replaces them with // others). struct IIAAKillOrReplaceEF { using l_t = typename AnalysisDomainTy::l_t; - l_t Replacement{}; + l_t Replacement = BitVectorSet(); l_t computeTarget(ByConstRef /* Src */) const { return Replacement; } @@ -969,27 +961,13 @@ class IDEInstInteractionAnalysisT "IIAAKillOrReplaceEF is too large for SOO"); if (auto *AD = llvm::dyn_cast(SecondFunction)) { - auto ADCache = - SecondFunction.template getCacheOrNull(); - assert(ADCache != nullptr); - if (This->isKillAll()) { - return ADCache->createEdgeFunction(*AD); - } auto Union = IDEInstInteractionAnalysisT::joinImpl(This->Replacement, AD->Data); - return ADCache->createEdgeFunction(std::move(Union)); + return Cache->createEdgeFunction(std::move(Union)); } if (auto *KR = llvm::dyn_cast(SecondFunction)) { - if (This->isKillAll()) { - return Cache->createEdgeFunction(*KR); - } - if (KR->isKillAll()) { - return SecondFunction; - } - auto Union = IDEInstInteractionAnalysisT::joinImpl(This->Replacement, - KR->Replacement); - return Cache->createEdgeFunction(std::move(Union)); + return SecondFunction; } llvm::report_fatal_error( "found unexpected edge function in 'IIAAKillOrReplaceEF'"); @@ -1030,6 +1008,8 @@ class IDEInstInteractionAnalysisT return Replacement == Other.Replacement; } + [[nodiscard]] bool isConstant() const noexcept { return true; } + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const IIAAKillOrReplaceEF &EF) { OS << "EF: (IIAAKillOrReplaceEF)<->"; @@ -1058,7 +1038,7 @@ class IDEInstInteractionAnalysisT // add all labels provided by Data. struct IIAAAddLabelsEF { using l_t = typename AnalysisDomainTy::l_t; - l_t Data{}; + l_t Data = BitVectorSet(); l_t computeTarget(ByConstRef Src) const { return IDEInstInteractionAnalysisT::joinImpl(Src, Data); @@ -1080,7 +1060,7 @@ class IDEInstInteractionAnalysisT return Cache->createEdgeFunction(std::move(Union)); } if (auto *KR = llvm::dyn_cast(SecondFunction)) { - return Cache->createEdgeFunction(KR->Replacement); + return SecondFunction; } llvm::report_fatal_error( "found unexpected edge function in 'IIAAAddLabelsEF'"); @@ -1134,23 +1114,6 @@ class IDEInstInteractionAnalysisT // Provide functionalities for printing things and emitting text reports. - void printNode(llvm::raw_ostream &OS, n_t n) const override { - OS << llvmIRToString(n); - } - - void printDataFlowFact(llvm::raw_ostream &OS, d_t FlowFact) const override { - OS << llvmIRToString(FlowFact); - } - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override { - OS << Fun->getName(); - } - - inline void printEdgeFact(llvm::raw_ostream &OS, - l_t EdgeFact) const override { - printEdgeFactImpl(OS, EdgeFact); - } - static void stripBottomResults(std::unordered_map &Res) { for (auto It = Res.begin(); It != Res.end();) { if (It->second.isBottom()) { @@ -1177,11 +1140,11 @@ class IDEInstInteractionAnalysisT auto Results = SR.resultsAt(Inst, true); stripBottomResults(Results); if (!Results.empty()) { - OS << "At IR statement: " << this->NtoString(Inst) << '\n'; + OS << "At IR statement: " << NToString(Inst) << '\n'; for (auto Result : Results) { if (!Result.second.isBottom()) { - OS << " Fact: " << this->DtoString(Result.first) - << "\n Value: " << this->LtoString(Result.second) << '\n'; + OS << " Fact: " << DToString(Result.first) + << "\n Value: " << LToString(Result.second) << '\n'; } } OS << '\n'; @@ -1228,9 +1191,8 @@ class IDEInstInteractionAnalysisT } protected: - static inline bool isZeroValueImpl(d_t d) { - return LLVMZeroValue::isLLVMZeroValue(d); - } + // NOLINTNEXTLINE(readability-identifier-naming) + static constexpr auto isZeroValueImpl = LLVMZeroValue::isLLVMZeroValue; static void printEdgeFactImpl(llvm::raw_ostream &OS, ByConstRef EdgeFact) { @@ -1258,10 +1220,10 @@ class IDEInstInteractionAnalysisT } static inline l_t joinImpl(ByConstRef Lhs, ByConstRef Rhs) { - if (Lhs.isTop() || Lhs.isBottom()) { + if (Lhs.isTop() || Rhs.isBottom()) { return Rhs; } - if (Rhs.isTop() || Rhs.isBottom()) { + if (Rhs.isTop() || Lhs.isBottom()) { return Lhs; } const auto &LhsSet = std::get>(Lhs); @@ -1284,7 +1246,7 @@ class IDEInstInteractionAnalysisT // at some point. Therefore, we only care for the variables and their // associated values and ignore at which point a variable may holds as a // data-flow fact. - const auto Variable = Result.getColumnKey(); + const auto &Variable = Result.getColumnKey(); const auto &Value = Result.getValue(); // skip result entry if variable is not in the set of all variables if (Variables.find(Variable) == Variables.end()) { diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h index ec1b4beea..2e7c6d873 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDELINEARCONSTANTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/Domain/LatticeDomain.h" @@ -94,7 +94,7 @@ class IDELinearConstantAnalysis [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; // in addition provide specifications for the IDE parts @@ -117,18 +117,8 @@ class IDELinearConstantAnalysis EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, d_t SuccNode) override; - EdgeFunction allTopFunction() override; - // Helper functions - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; - [[nodiscard]] lca_results_t getLCAResults(SolverResults SR); void emitTextReport(const SolverResults &SR, diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEProtoAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEProtoAnalysis.h index fffad0dd6..c4b9ab965 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEProtoAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEProtoAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEPROTOANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDEPROTOANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEPROTOANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEPROTOANALYSIS_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -66,7 +66,7 @@ class IDEProtoAnalysis : public IDETabulationProblem { [[nodiscard]] d_t createZeroValue() const; - bool isZeroValue(d_t Fact) const override; + bool isZeroValue(d_t Fact) const noexcept override; // in addition provide specifications for the IDE parts @@ -97,14 +97,6 @@ class IDEProtoAnalysis : public IDETabulationProblem { l_t join(l_t Lhs, l_t Rhs) override; EdgeFunction allTopFunction() override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h index 7bc28bbdf..81036cdbe 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h @@ -7,10 +7,11 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDESECUREHEAPPROPAGATION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDESECUREHEAPPROPAGATION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDESECUREHEAPPROPAGATION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDESECUREHEAPPROPAGATION_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "llvm/ADT/StringRef.h" @@ -80,13 +81,7 @@ class IDESecureHeapPropagation [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; // in addition provide specifications for the IDE parts @@ -110,19 +105,12 @@ class IDESecureHeapPropagation n_t RetSite, d_t RetSiteNode) override; - l_t topElement() override; - - l_t bottomElement() override; - - l_t join(l_t Lhs, l_t Rhs) override; - - EdgeFunction allTopFunction() override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; - void emitTextReport(const SolverResults &SR, llvm::raw_ostream &OS) override; }; + +llvm::StringRef DToString(SecureHeapFact Fact) noexcept; +llvm::StringRef LToString(SecureHeapValue Val) noexcept; } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h index 6ae2e387a..3c1736fb5 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESolverTest.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDESOLVERTEST_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDESOLVERTEST_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDESOLVERTEST_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDESOLVERTEST_H #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -67,7 +67,7 @@ class IDESolverTest : public IDETabulationProblem { [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; // in addition provide specifications for the IDE parts @@ -98,14 +98,6 @@ class IDESolverTest : public IDETabulationProblem { l_t join(l_t Lhs, l_t Rhs) override; EdgeFunction allTopFunction() override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index 2d46060ed..be868e560 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -7,41 +7,152 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDETYPESTATEANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IDETYPESTATEANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDETYPESTATEANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDETYPESTATEANALYSIS_H +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" - -#include "llvm/IR/InstrTypes.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Value.h" #include #include #include +#include #include -namespace llvm { -class CallBase; -class Instruction; -class Function; -class Value; -} // namespace llvm - namespace psr { class LLVMBasedICFG; class LLVMTypeHierarchy; -struct TypeStateDescription; +namespace detail { + +class IDETypeStateAnalysisBaseCommon : public LLVMAnalysisDomainDefault { +public: + using container_type = std::set; + using FlowFunctionPtrType = FlowFunctionPtrType; +}; + +class IDETypeStateAnalysisBase + : private IDETypeStateAnalysisBaseCommon, + private FlowFunctionTemplates< + IDETypeStateAnalysisBaseCommon::d_t, + IDETypeStateAnalysisBaseCommon::container_type> { +public: + virtual ~IDETypeStateAnalysisBase() = default; + +protected: + IDETypeStateAnalysisBase(LLVMAliasInfoRef PT) noexcept : PT(PT) {} + + using typename IDETypeStateAnalysisBaseCommon::container_type; + using typename IDETypeStateAnalysisBaseCommon::d_t; + using typename IDETypeStateAnalysisBaseCommon::f_t; + using typename IDETypeStateAnalysisBaseCommon::FlowFunctionPtrType; + using typename IDETypeStateAnalysisBaseCommon::n_t; + + // --- Flow Functions + + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ); + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun); + FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, + n_t ExitStmt, n_t RetSite); + FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees); + FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun); + + // --- Utilities + + [[nodiscard]] virtual bool + isAPIFunction(llvm::StringRef Name) const noexcept = 0; + [[nodiscard]] virtual bool + isFactoryFunction(llvm::StringRef Name) const noexcept = 0; + [[nodiscard]] virtual bool + isTypeNameOfInterest(llvm::StringRef Name) const noexcept = 0; + + /** + * @brief Returns all alloca's that are (indirect) aliases of V. + * + * Currently PhASAR's points-to information does not include alloca + * instructions, since alloca instructions, i.e. memory locations, are of + * type T* for a target type T. Thus they do not alias directly. Therefore, + * for each alias of V we collect related alloca instructions by checking + * load and store instructions for used alloca's. + */ + container_type getRelevantAllocas(d_t V); + + /** + * @brief Returns whole-module aliases of V. + * + * This function retrieves whole-module points-to information. We store + * already computed points-to information in a cache to prevent expensive + * recomputation since the whole module points-to graph can be huge. This + * might become unnecessary once PhASAR's AliasGraph starts using a cache + * itself. + */ + container_type getWMAliasSet(d_t V); + + /** + * @brief Provides whole module aliases and relevant alloca's of V. + */ + container_type getWMAliasesAndAllocas(d_t V); + + /** + * @brief Provides local aliases and relevant alloca's of V. + */ + container_type getLocalAliasesAndAllocas(d_t V, llvm::StringRef Fname); + + /** + * @brief Checks if the type machtes the type of interest. + */ + bool hasMatchingType(d_t V); + +private: + FlowFunctionPtrType generateFromZero(d_t FactToGenerate) { + return generateFlow(FactToGenerate, LLVMZeroValue::getInstance()); + } + + bool hasMatchingTypeName(const llvm::Type *Ty); + + std::map AliasCache; + LLVMAliasInfoRef PT{}; + std::map> + RelevantAllocaCache; +}; +} // namespace detail + +template struct IDETypeStateAnalysisDomain : public LLVMAnalysisDomainDefault { - using l_t = int; + using l_t = typename TypeStateDescriptionTy::State; }; +template class IDETypeStateAnalysis - : public IDETabulationProblem { + : public IDETabulationProblem< + IDETypeStateAnalysisDomain>, + private detail::IDETypeStateAnalysisBase { public: - using IDETabProblemType = IDETabulationProblem; + using IDETabProblemType = + IDETabulationProblem>; + using typename IDETabProblemType::container_type; using typename IDETabProblemType::d_t; using typename IDETabProblemType::f_t; using typename IDETabProblemType::i_t; @@ -50,61 +161,317 @@ class IDETypeStateAnalysis using typename IDETabProblemType::t_t; using typename IDETabProblemType::v_t; - using ConfigurationTy = TypeStateDescription; + using typename IDETabProblemType::FlowFunctionPtrType; + using ConfigurationTy = TypeStateDescriptionTy; +private: + static AllBottom + makeAllBottom(const TypeStateDescriptionTy *TSD) noexcept { + if constexpr (HasJoinLatticeTraits) { + return AllBottom{}; + } else { + return AllBottom{TSD->bottom()}; + } + } + template >> + static AllBottom makeAllBottom(EmptyType /*unused*/) noexcept { + return AllBottom{}; + } + static bool isBottom(l_t State, const TypeStateDescriptionTy *TSD) noexcept { + if constexpr (HasJoinLatticeTraits) { + return State == JoinLatticeTraits::bottom(); + } else { + return State == TSD->bottom(); + } + } + template >> + static bool isBottom(l_t State, EmptyType /*unused*/) noexcept { + return State == JoinLatticeTraits::bottom(); + } + + struct TSEdgeFunctionComposer : EdgeFunctionComposer { + TSEdgeFunctionComposer(EdgeFunction First, EdgeFunction Second, + const TypeStateDescriptionTy *TSD) noexcept + : EdgeFunctionComposer{std::move(First), std::move(Second)} { + if constexpr (!HasJoinLatticeTraits) { + BotElement = TSD->bottom(); + } + } + + [[no_unique_address]] std::conditional_t, + EmptyType, l_t> + BotElement{}; + + static EdgeFunction join(EdgeFunctionRef This, + const EdgeFunction &OtherFunction) { + if (auto Default = defaultJoinOrNull(This, OtherFunction)) { + return Default; + } + if constexpr (HasJoinLatticeTraits) { + return AllBottom{}; + } else { + return AllBottom{This->BotElement}; + } + } + }; + + struct TSEdgeFunction { + using l_t = l_t; + const TypeStateDescriptionTy *TSD{}; + // XXX: Do we really need a string here? Can't we just use an integer or sth + // else that is cheap? + std::string Token; + const llvm::CallBase *CallSite{}; + + [[nodiscard]] l_t computeTarget(l_t Source) const { + + // assert((Source != TSD->top()) && "Error: call computeTarget with + // TOP\n"); + + auto CurrentState = TSD->getNextState( + Token, Source == TSD->top() ? TSD->uninit() : Source, CallSite); + PHASAR_LOG_LEVEL(DEBUG, "State machine transition: (" + << Token << " , " << LToString(Source) + << ") -> " << LToString(CurrentState)); + return CurrentState; + } + + static EdgeFunction compose(EdgeFunctionRef This, + const EdgeFunction &SecondFunction) { + if (auto Default = defaultComposeOrNull(This, SecondFunction)) { + return Default; + } + + return TSEdgeFunctionComposer{This, SecondFunction, This->TSD}; + } + + static EdgeFunction join(EdgeFunctionRef This, + const EdgeFunction &OtherFunction) { + if (auto Default = defaultJoinOrNull(This, OtherFunction)) { + return Default; + } + + return makeAllBottom(This->TSD); + } + + bool operator==(const TSEdgeFunction &Other) const { + return CallSite == Other.CallSite && Token == Other.Token; + } + + friend llvm::raw_ostream &print(llvm::raw_ostream &OS, + const TSEdgeFunction &TSE) { + return OS << "TSEF(" << TSE.Token << " at " + << llvmIRToShortString(TSE.CallSite) << ")"; + } + }; + + struct TSConstant : ConstantEdgeFunction { + std::conditional_t, EmptyType, + const TypeStateDescriptionTy *> + TSD{}; + + TSConstant(l_t Value, const TypeStateDescriptionTy *TSD) noexcept + : ConstantEdgeFunction{Value} { + if constexpr (!HasJoinLatticeTraits) { + this->TSD = TSD; + } + } + + template >> + TSConstant(l_t Value, EmptyType /*unused*/ = {}) noexcept + : ConstantEdgeFunction{Value} { + if constexpr (!HasJoinLatticeTraits) { + this->TSD = TSD; + } + } + + /// XXX: Cannot default compose() and join(), because l_t does not implement + /// JoinLatticeTraits (because bottom value is not constant) + template + static EdgeFunction compose(EdgeFunctionRef This, + const EdgeFunction &SecondFunction) { + + if (auto Default = defaultComposeOrNull(This, SecondFunction)) { + return Default; + } + + l_t Ret = SecondFunction.computeTarget(This->Value); + if (Ret == This->Value) { + return This; + } + if (isBottom(Ret, This->TSD)) { + return makeAllBottom(This->TSD); + } + + return TSConstant{Ret, This->TSD}; + } + + template + static EdgeFunction join(EdgeFunctionRef This, + const EdgeFunction &OtherFunction) { + if (auto Default = defaultJoinOrNull(This, OtherFunction)) { + return Default; + } + + auto Top = [TSD = This->TSD] { + if constexpr (HasJoinLatticeTraits) { + return JoinLatticeTraits::top(); + } else { + return TSD->top(); + } + }(); + if (const auto *C = llvm::dyn_cast(OtherFunction)) { + if (C->Value == This->Value || C->Value == Top) { + return This; + } + if (This->Value == Top) { + return OtherFunction; + } + } + return makeAllBottom(This->TSD); + } + + bool operator==(const TSConstant &Other) const noexcept { + return this->Value == Other.Value; + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const TSConstant &EF) { + return OS << "TSConstant[" << LToString(EF.Value) << "]"; + } + }; + +public: IDETypeStateAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, - const TypeStateDescription *TSD, - std::vector EntryPoints = {"main"}); + const TypeStateDescriptionTy *TSD, + std::vector EntryPoints = {"main"}) + : IDETabProblemType(IRDB, std::move(EntryPoints), createZeroValue()), + IDETypeStateAnalysisBase(PT), TSD(TSD) { + assert(TSD != nullptr); + assert(PT); + } ~IDETypeStateAnalysis() override = default; // start formulating our analysis by specifying the parts required for IFDS - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { + return detail::IDETypeStateAnalysisBase::getNormalFlowFunction(Curr, Succ); + } - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override; + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override { + return detail::IDETypeStateAnalysisBase::getCallFlowFunction(CallSite, + DestFun); + } FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitStmt, n_t RetSite) override; + n_t ExitStmt, n_t RetSite) override { + + return detail::IDETypeStateAnalysisBase::getRetFlowFunction( + CallSite, CalleeFun, ExitStmt, RetSite); + } FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) override; + llvm::ArrayRef Callees) override { + return detail::IDETypeStateAnalysisBase::getCallToRetFlowFunction( + CallSite, RetSite, Callees); + } FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, - f_t DestFun) override; + f_t DestFun) override { + return detail::IDETypeStateAnalysisBase::getSummaryFlowFunction(CallSite, + DestFun); + } - InitialSeeds initialSeeds() override; + InitialSeeds initialSeeds() override { + return this->createDefaultSeeds(); + } - [[nodiscard]] d_t createZeroValue() const; + [[nodiscard]] d_t createZeroValue() const { + return LLVMZeroValue::getInstance(); + } - [[nodiscard]] bool isZeroValue(d_t Fact) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override { + return LLVMZeroValue::isLLVMZeroValue(Fact); + } // in addition provide specifications for the IDE parts - EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) override; - - EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t DestinationFunction, - d_t DestNode) override; - - EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, - n_t ExitInst, d_t ExitNode, - n_t RetSite, d_t RetNode) override; + EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t /*Succ*/, + d_t SuccNode) override { + // Set alloca instructions of target type to uninitialized. + if (const auto *Alloca = llvm::dyn_cast(Curr)) { + if (hasMatchingType(Alloca)) { + if (LLVMZeroValue::isLLVMZeroValue(CurrNode) && SuccNode == Alloca) { + return TSConstant(TSD->uninit(), TSD); + } + } + } + return EdgeIdentity{}; + } + + EdgeFunction getCallEdgeFunction(n_t /*CallSite*/, d_t /*SrcNode*/, + f_t /*DestinationFunction*/, + d_t /*DestNode*/) override { + return EdgeIdentity{}; + } + + EdgeFunction getReturnEdgeFunction(n_t /*CallSite*/, + f_t /*CalleeFunction*/, + n_t /*ExitInst*/, d_t /*ExitNode*/, + n_t /*RetSite*/, + d_t /*RetNode*/) override { + return EdgeIdentity{}; + } EdgeFunction - getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, + getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t /*RetSite*/, d_t RetSiteNode, - llvm::ArrayRef Callees) override; - - EdgeFunction getSummaryEdgeFunction(n_t CallSite, d_t CallNode, - n_t RetSite, - d_t RetSiteNode) override; - - l_t topElement() override; - - l_t bottomElement() override; + llvm::ArrayRef Callees) override { + const auto *CS = llvm::cast(CallSite); + for (const auto *Callee : Callees) { + std::string DemangledFname = llvm::demangle(Callee->getName().str()); + + // For now we assume that we can only generate from the return value. + // We apply the same edge function for the return value, i.e. callsite. + if (TSD->isFactoryFunction(DemangledFname)) { + PHASAR_LOG_LEVEL(DEBUG, "Processing factory function"); + if (isZeroValue(CallNode) && RetSiteNode == CS) { + return TSConstant{ + TSD->getNextState(DemangledFname, TSD->uninit(), CS), TSD}; + } + } + + // For every consuming parameter and all its aliases and relevant alloca's + // we apply the same edge function. + if (TSD->isConsumingFunction(DemangledFname)) { + PHASAR_LOG_LEVEL(DEBUG, "Processing consuming function"); + for (auto Idx : TSD->getConsumerParamIdx(DemangledFname)) { + const auto &AliasAndAllocas = + getWMAliasesAndAllocas(CS->getArgOperand(Idx)); + + if (CallNode == RetSiteNode && AliasAndAllocas.count(CallNode)) { + return TSEdgeFunction{TSD, DemangledFname, CS}; + } + } + } + } + return EdgeIdentity{}; + } + + EdgeFunction getSummaryEdgeFunction(n_t /*CallSite*/, d_t /*CallNode*/, + n_t /*RetSite*/, + d_t /*RetSiteNode*/) override { + return nullptr; + } + + l_t topElement() override { return TSD->top(); } + + l_t bottomElement() override { return TSD->bottom(); } /** * We have a lattice with BOTTOM representing all information @@ -114,65 +481,93 @@ class IDETypeStateAnalysis * * @note Only one-level lattice's are handled currently */ - l_t join(l_t Lhs, l_t Rhs) override; - - EdgeFunction allTopFunction() override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; - - void printEdgeFact(llvm::raw_ostream &OS, l_t L) const override; + l_t join(l_t Lhs, l_t Rhs) override { + if (Lhs == Rhs) { + return Lhs; + } + if (Lhs == TSD->top()) { + return Rhs; + } + if (Rhs == TSD->top()) { + return Lhs; + } + return TSD->bottom(); + } + + EdgeFunction allTopFunction() override { + if constexpr (HasJoinLatticeTraits) { + return AllTop{}; + } else { + return AllTop{topElement()}; + } + } + + [[nodiscard]] bool + isAPIFunction(llvm::StringRef Name) const noexcept override { + return TSD->isAPIFunction(Name); + } + + [[nodiscard]] bool + isFactoryFunction(llvm::StringRef Name) const noexcept override { + return TSD->isFactoryFunction(Name); + } + + [[nodiscard]] bool + isTypeNameOfInterest(llvm::StringRef Name) const noexcept override { + return Name.contains(TSD->getTypeNameOfInterest()); + } void emitTextReport(const SolverResults &SR, - llvm::raw_ostream &OS = llvm::outs()) override; + llvm::raw_ostream &OS = llvm::outs()) override { + LLVMBasedCFG CFG; + for (const auto &F : this->IRDB->getAllFunctions()) { + for (const auto &BB : *F) { + for (const auto &I : BB) { + auto Results = SR.resultsAt(&I, true); + + if (CFG.isExitInst(&I)) { + for (auto Res : Results) { + if (const auto *Alloca = + llvm::dyn_cast(Res.first)) { + if (Res.second == TSD->error()) { + // ERROR STATE DETECTED + this->Printer->onResult(&I, Res.first, TSD->error(), + TSD->analysisType()); + } + } + } + } else { + for (auto Res : Results) { + if (const auto *Alloca = + llvm::dyn_cast(Res.first)) { + if (Res.second == TSD->error()) { + // ERROR STATE DETECTED + this->Printer->onResult(&I, Res.first, TSD->error(), + TSD->analysisType()); + } + } + } + } + } + } + } + + this->Printer->onFinalize(); + } private: - const TypeStateDescription *TSD{}; - std::map AliasCache; - LLVMAliasInfoRef PT{}; - std::map> - RelevantAllocaCache; - - /** - * @brief Returns all alloca's that are (indirect) aliases of V. - * - * Currently PhASAR's points-to information does not include alloca - * instructions, since alloca instructions, i.e. memory locations, are of - * type T* for a target type T. Thus they do not alias directly. Therefore, - * for each alias of V we collect related alloca instructions by checking - * load and store instructions for used alloca's. - */ - std::set getRelevantAllocas(d_t V); - - /** - * @brief Returns whole-module aliases of V. - * - * This function retrieves whole-module points-to information. We store - * already computed points-to information in a cache to prevent expensive - * recomputation since the whole module points-to graph can be huge. This - * might become unnecessary once PhASAR's AliasGraph starts using a cache - * itself. - */ - std::set getWMAliasSet(d_t V); + const TypeStateDescriptionTy *TSD{}; +}; - /** - * @brief Provides whole module aliases and relevant alloca's of V. - */ - std::set getWMAliasesAndAllocas(d_t V); +template +IDETypeStateAnalysis(const LLVMProjectIRDB *, LLVMAliasInfoRef, + const TypeStateDescriptionTy *, + std::vector EntryPoints) + -> IDETypeStateAnalysis; - /** - * @brief Provides local aliases and relevant alloca's of V. - */ - std::set getLocalAliasesAndAllocas(d_t V, const std::string &Fname); +// class CSTDFILEIOTypeStateDescription; - /** - * @brief Checks if the type machtes the type of interest. - */ - bool hasMatchingType(d_t V); -}; +// extern template class IDETypeStateAnalysis; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h index 85ca53443..590513a44 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSCONSTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSCONSTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSCONSTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSCONSTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -142,13 +142,7 @@ class IFDSConstAnalysis */ [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; void emitTextReport(const SolverResults &SR, llvm::raw_ostream &OS = llvm::outs()) override; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSFieldSensTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSFieldSensTaintAnalysis.h index d64c3993f..135a51811 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSFieldSensTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSFieldSensTaintAnalysis.h @@ -2,8 +2,8 @@ * @author Sebastian Roland */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSFIELDSENSTAINTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSFIELDSENSTAINTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSFIELDSENSTAINTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSFIELDSENSTAINTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde//IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h" @@ -81,45 +81,18 @@ class IFDSFieldSensTaintAnalysis return ExtendedValue(LLVMZeroValue::getInstance()); } - [[nodiscard]] bool isZeroValue(ExtendedValue EV) const override { + [[nodiscard]] bool isZeroValue(ExtendedValue EV) const noexcept override { return LLVMZeroValue::isLLVMZeroValue(EV.getValue()); } - void printNode(llvm::raw_ostream &OS, - const llvm::Instruction *Stmt) const override { - OS << llvmIRToString(Stmt); - } - - void printDataFlowFact(llvm::raw_ostream &OS, - ExtendedValue EV) const override { - OS << llvmIRToString(EV.getValue()) << "\n"; - for (const auto *MemLocationPart : EV.getMemLocationSeq()) { - OS << "A:\t" << llvmIRToString(MemLocationPart) << "\n"; - } - if (!EV.getEndOfTaintedBlockLabel().empty()) { - OS << "L:\t" << EV.getEndOfTaintedBlockLabel() << "\n"; - } - if (EV.isVarArg()) { - OS << "VT:\t" << EV.isVarArgTemplate() << "\n"; - for (const auto *VAListMemLocationPart : EV.getVaListMemLocationSeq()) { - OS << "VLA:\t" << llvmIRToString(VAListMemLocationPart) << "\n"; - } - OS << "VI:\t" << EV.getVarArgIndex() << "\n"; - OS << "CI:\t" << EV.getCurrentVarArgIndex() << "\n"; - } - } - - void printFunction(llvm::raw_ostream &OS, - const llvm::Function *Func) const override { - OS << Func->getName(); - } - private: const LLVMTaintConfig *Config{}; TraceStats Stats{}; }; +std::string DToString(const ExtendedValue &EV); + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSProtoAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSProtoAnalysis.h index ee628992c..fb1db483c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSProtoAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSProtoAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSPROTOANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSPROTOANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSPROTOANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSPROTOANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -52,13 +52,7 @@ class IFDSProtoAnalysis [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSignAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSignAnalysis.h index a97e4a9c9..54338de63 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSignAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSignAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSSIGNANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSSIGNANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSSIGNANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSSIGNANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -53,13 +53,7 @@ class IFDSSignAnalysis [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSolverTest.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSolverTest.h index 69c0ea3eb..edf176f77 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSSolverTest.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSSOLVERTEST_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSSOLVERTEST_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSSOLVERTEST_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSSOLVERTEST_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -51,13 +51,7 @@ class IFDSSolverTest [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h index 7054f4165..6c58a032d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -55,7 +55,8 @@ class IFDSTaintAnalysis */ IFDSTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, const LLVMTaintConfig *Config, - std::vector EntryPoints = {"main"}); + std::vector EntryPoints = {"main"}, + bool TaintMainArgs = true); ~IFDSTaintAnalysis() override = default; @@ -77,13 +78,7 @@ class IFDSTaintAnalysis [[nodiscard]] d_t createZeroValue() const; - bool isZeroValue(d_t FlowFact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t FlowFact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; + bool isZeroValue(d_t FlowFact) const noexcept override; void emitTextReport(const SolverResults &SR, llvm::raw_ostream &OS = llvm::outs()) override; @@ -91,6 +86,7 @@ class IFDSTaintAnalysis private: const LLVMTaintConfig *Config{}; LLVMAliasInfoRef PT{}; + bool TaintMainArgs{}; bool isSourceCall(const llvm::CallBase *CB, const llvm::Function *Callee) const; @@ -98,8 +94,10 @@ class IFDSTaintAnalysis bool isSanitizerCall(const llvm::CallBase *CB, const llvm::Function *Callee) const; - void populateWithMayAliases(std::set &Facts) const; - void populateWithMustAliases(std::set &Facts) const; + void populateWithMayAliases(container_type &Facts, + const llvm::Instruction *Context) const; + void populateWithMustAliases(container_type &Facts, + const llvm::Instruction *Context) const; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.h index b8b4e060c..8f5c8c04f 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSTYPEANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSTYPEANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTYPEANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTYPEANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -44,13 +44,7 @@ class IFDSTypeAnalysis [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h index 46db4b631..c6a92f457 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSUNINITIALIZEDVARIABLES_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_IFDSUNINITIALIZEDVARIABLES_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSUNINITIALIZEDVARIABLES_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSUNINITIALIZEDVARIABLES_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -60,13 +60,7 @@ class IFDSUninitializedVariables [[nodiscard]] d_t createZeroValue() const; - [[nodiscard]] bool isZeroValue(d_t Fact) const override; - - void printNode(llvm::raw_ostream &OS, n_t Stmt) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Func) const override; + [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; void emitTextReport(const SolverResults &Results, llvm::raw_ostream &OS = llvm::outs()) override; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h index 88e29ef1a..c7cad7975 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h @@ -7,9 +7,10 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_CSTDFILEIOTYPESTATEDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_CSTDFILEIOTYPESTATEDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_CSTDFILEIOTYPESTATEDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_CSTDFILEIOTYPESTATEDESCRIPTION_H +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" #include @@ -18,62 +19,64 @@ namespace psr { +enum class CSTDFILEIOState { + TOP = 42, + UNINIT = 0, + OPENED = 1, + CLOSED = 2, + ERROR = 3, + BOT = 4 +}; +llvm::StringRef to_string(CSTDFILEIOState State) noexcept; +template <> struct JoinLatticeTraits { + static constexpr CSTDFILEIOState top() noexcept { + return CSTDFILEIOState::TOP; + } + static constexpr CSTDFILEIOState bottom() noexcept { + return CSTDFILEIOState::BOT; + } + static constexpr CSTDFILEIOState join(CSTDFILEIOState L, + CSTDFILEIOState R) noexcept { + if (L == top() || R == bottom()) { + return R; + } + if (L == bottom() || R == top()) { + return L; + } + return bottom(); + } +}; + /** * A type state description for C's file I/O API. The finite state machine * is encoded by a two-dimensional array with rows as function tokens and * columns as states. */ -class CSTDFILEIOTypeStateDescription : public TypeStateDescription { -private: - /** - * We use the following lattice - * BOT = all information - * - * UNINIT OPENED CLOSED ERROR - * - * TOP = no information - */ - enum CSTDFILEIOState { - TOP = 42, - UNINIT = 0, - OPENED = 1, - CLOSED = 2, - ERROR = 3, - BOT = 4 - }; - - /** - * The STAR token represents all API functions besides fopen(), fdopen() and - * fclose(). FOPEN covers fopen() and fdopen() since both functions are - * modeled the same in our case. - */ - enum class CSTDFILEIOToken { FOPEN = 0, FCLOSE = 1, STAR = 2 }; - - static const std::map> StdFileIOFuncs; - // delta matrix to implement the state machine's delta function - static const CSTDFILEIOState Delta[3][5]; - static CSTDFILEIOToken funcNameToToken(const std::string &F); - +class CSTDFILEIOTypeStateDescription + : public TypeStateDescription { public: - [[nodiscard]] bool isFactoryFunction(const std::string &F) const override; - [[nodiscard]] bool isConsumingFunction(const std::string &F) const override; - [[nodiscard]] bool isAPIFunction(const std::string &F) const override; + using TypeStateDescription::getNextState; + [[nodiscard]] bool isFactoryFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isConsumingFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isAPIFunction(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State - getNextState(std::string Tok, TypeStateDescription::State S) const override; + getNextState(llvm::StringRef Tok, + TypeStateDescription::State S) const override; [[nodiscard]] std::string getTypeNameOfInterest() const override; [[nodiscard]] std::set - getConsumerParamIdx(const std::string &F) const override; + getConsumerParamIdx(llvm::StringRef F) const override; [[nodiscard]] std::set - getFactoryParamIdx(const std::string &F) const override; - [[nodiscard]] std::string - stateToString(TypeStateDescription::State S) const override; + getFactoryParamIdx(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State bottom() const override; [[nodiscard]] TypeStateDescription::State top() const override; [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; +extern template class IDETypeStateAnalysis; + } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h index 580a4011a..144d3571c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.h @@ -7,15 +7,14 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFCTXDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFCTXDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFCTXDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFCTXDESCRIPTION_H #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/Domain/AnalysisDomain.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h" -#include #include #include #include @@ -27,32 +26,35 @@ class Value; namespace psr { +/** + * We use the following lattice + * BOT = all information + * + * UNINIT CTX_ATTACHED PARAM_INIT DERIVED ERROR + * + * TOP = no information + */ +enum class OpenSSLEVPKDFCTXState { + TOP = 42, + UNINIT = 5, + CTX_ATTACHED = 1, + PARAM_INIT = 2, + DERIVED = 3, + ERROR = 4, + BOT = 0 // It is VERY IMPORTANT, athat BOT has value 0, since this is the + // default value +}; + +llvm::StringRef to_string(OpenSSLEVPKDFCTXState State) noexcept; + /** * A type state description for OpenSSL's EVP Key Derivation functions. The * finite state machine is encoded by a two-dimensional array with rows as * function tokens and columns as states. */ -class OpenSSLEVPKDFCTXDescription : public TypeStateDescription { +class OpenSSLEVPKDFCTXDescription + : public TypeStateDescription { private: - /** - * We use the following lattice - * BOT = all information - * - * UNINIT CTX_ATTACHED PARAM_INIT DERIVED ERROR - * - * TOP = no information - */ - enum OpenSSLEVPKDFState { - TOP = 42, - UNINIT = 5, - CTX_ATTACHED = 1, - PARAM_INIT = 2, - DERIVED = 3, - ERROR = 4, - BOT = 0 // It is VERY IMPORTANT, athat BOT has value 0, since this is the - // default value - }; - /** * The STAR token represents all functions besides EVP_KDF_fetch(), * EVP_KDF_CTX_new(), EVP_KDF_CTX_set_params() ,derive() and @@ -66,42 +68,41 @@ class OpenSSLEVPKDFCTXDescription : public TypeStateDescription { STAR = 4 }; - static const std::map> OpenSSLEVPKDFFuncs; // Delta matrix to implement the state machine's Delta function - static const OpenSSLEVPKDFState Delta[5][6]; + static const OpenSSLEVPKDFCTXState Delta[5][6]; // std::map, int> // requiredKDFState; - IDESolver &KDFAnalysisResults; - static OpenSSLEVTKDFToken funcNameToToken(const std::string &F); + IDESolver> + &KDFAnalysisResults; + static OpenSSLEVTKDFToken funcNameToToken(llvm::StringRef F); public: + using TypeStateDescription::getNextState; OpenSSLEVPKDFCTXDescription( - IDESolver &KDFAnalysisResults) + IDESolver> + &KDFAnalysisResults) : KDFAnalysisResults(KDFAnalysisResults) {} + [[nodiscard]] bool isFactoryFunction(llvm::StringRef FuncName) const override; [[nodiscard]] bool - isFactoryFunction(const std::string &FuncName) const override; - [[nodiscard]] bool - isConsumingFunction(const std::string &FuncName) const override; - [[nodiscard]] bool isAPIFunction(const std::string &FuncName) const override; - [[nodiscard]] TypeStateDescription::State - getNextState(std::string Tok, TypeStateDescription::State S) const override; - [[nodiscard]] TypeStateDescription::State - getNextState(const std::string &Tok, TypeStateDescription::State S, + isConsumingFunction(llvm::StringRef FuncName) const override; + [[nodiscard]] bool isAPIFunction(llvm::StringRef FuncName) const override; + [[nodiscard]] State getNextState(llvm::StringRef Tok, State S) const override; + [[nodiscard]] State + getNextState(llvm::StringRef Tok, State S, const llvm::CallBase *CallSite) const override; [[nodiscard]] std::string getTypeNameOfInterest() const override; [[nodiscard]] std::set - getConsumerParamIdx(const std::string &F) const override; + getConsumerParamIdx(llvm::StringRef F) const override; [[nodiscard]] std::set - getFactoryParamIdx(const std::string &F) const override; - [[nodiscard]] std::string - stateToString(TypeStateDescription::State S) const override; - [[nodiscard]] TypeStateDescription::State bottom() const override; - [[nodiscard]] TypeStateDescription::State top() const override; - [[nodiscard]] TypeStateDescription::State uninit() const override; - [[nodiscard]] TypeStateDescription::State start() const override; - [[nodiscard]] TypeStateDescription::State error() const override; + getFactoryParamIdx(llvm::StringRef F) const override; + [[nodiscard]] State bottom() const override; + [[nodiscard]] State top() const override; + [[nodiscard]] State uninit() const override; + [[nodiscard]] State start() const override; + [[nodiscard]] State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; /* /// Checks all callSites, where a EVP_KDF object needs to be in a /// certain state, such that the state transition for EVP_KDF_CTX is valid. diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h index 96bd98864..324333c62 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h @@ -7,8 +7,8 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLEVPKDFDESCRIPTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" @@ -17,24 +17,28 @@ #include namespace psr { -class OpenSSLEVPKDFDescription : public TypeStateDescription { -public: - /** - * We use the following lattice - * BOT = all information - * - * UNINIT KDF_FETCHED ERROR - * - * TOP = no information - */ - enum OpenSSLEVPKDFState { - TOP = 42, - UNINIT = 0, - KDF_FETCHED = 1, - ERROR = 2, - BOT = 3 - }; +/** + * We use the following lattice + * BOT = all information + * + * UNINIT KDF_FETCHED ERROR + * + * TOP = no information + */ +enum class OpenSSLEVPKDFState { + TOP = 42, + UNINIT = 0, + KDF_FETCHED = 1, + ERROR = 2, + BOT = 3 +}; + +llvm::StringRef to_string(OpenSSLEVPKDFState State) noexcept; + +class OpenSSLEVPKDFDescription + : public TypeStateDescription { +public: /** * The STAR token represents all functions besides EVP_KDF_fetch(), * EVP_KDF_fetch() and EVP_KDF_CTX_free(). @@ -45,32 +49,32 @@ class OpenSSLEVPKDFDescription : public TypeStateDescription { STAR = 2 }; + using State = OpenSSLEVPKDFState; + private: - static const std::map> OpenSSLEVPKDFFuncs; // delta matrix to implement the state machine's delta function static const OpenSSLEVPKDFState Delta[3][4]; - static OpenSSLEVTKDFToken funcNameToToken(const std::string &F); + static OpenSSLEVTKDFToken funcNameToToken(llvm::StringRef F); public: - [[nodiscard]] bool isFactoryFunction(const std::string &F) const override; + using TypeStateDescription::getNextState; + [[nodiscard]] bool isFactoryFunction(llvm::StringRef F) const override; - [[nodiscard]] bool isConsumingFunction(const std::string &F) const override; + [[nodiscard]] bool isConsumingFunction(llvm::StringRef F) const override; - [[nodiscard]] bool isAPIFunction(const std::string &F) const override; + [[nodiscard]] bool isAPIFunction(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State - getNextState(std::string Tok, TypeStateDescription::State S) const override; + getNextState(llvm::StringRef Tok, + TypeStateDescription::State S) const override; [[nodiscard]] std::string getTypeNameOfInterest() const override; [[nodiscard]] std::set - getConsumerParamIdx(const std::string &F) const override; + getConsumerParamIdx(llvm::StringRef F) const override; [[nodiscard]] std::set - getFactoryParamIdx(const std::string &F) const override; - - [[nodiscard]] std::string - stateToString(TypeStateDescription::State S) const override; + getFactoryParamIdx(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State bottom() const override; @@ -81,6 +85,8 @@ class OpenSSLEVPKDFDescription : public TypeStateDescription { [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h index ca1205856..597fa2795 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.h @@ -7,32 +7,31 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREHEAPDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREHEAPDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREHEAPDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREHEAPDESCRIPTION_H #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/Domain/AnalysisDomain.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" -#include -#include #include namespace psr { +enum class OpenSSLSecureHeapState { + TOP = 42, + BOT = 0, + UNINIT = 1, + ALLOCATED = 2, + ZEROED = 3, + FREED = 4, + ERROR = 5 +}; +llvm::StringRef to_string(OpenSSLSecureHeapState State) noexcept; -class OpenSSLSecureHeapDescription : public TypeStateDescription { +class OpenSSLSecureHeapDescription + : public TypeStateDescription { private: - enum OpenSSLSecureHeapState { - TOP = 42, - BOT = 0, - UNINIT = 1, - ALLOCATED = 2, - ZEROED = 3, - FREED = 4, - ERROR = 5 - }; - enum class OpenSSLSecureHeapToken { SECURE_MALLOC = 0, SECURE_ZALLOC = 1, @@ -41,39 +40,39 @@ class OpenSSLSecureHeapDescription : public TypeStateDescription { STAR = 4 }; - static const std::map> OpenSSLSecureHeapFuncs; // Delta matrix to implement the state machine's Delta function static const OpenSSLSecureHeapState Delta[5][6]; IDESolver &SecureHeapPropagationResults; - static OpenSSLSecureHeapToken funcNameToToken(const std::string &F); + static OpenSSLSecureHeapToken funcNameToToken(llvm::StringRef F); public: + using TypeStateDescription::getNextState; OpenSSLSecureHeapDescription(IDESolver &SecureHeapPropagationResults); - [[nodiscard]] bool isFactoryFunction(const std::string &F) const override; - [[nodiscard]] bool isConsumingFunction(const std::string &F) const override; - [[nodiscard]] bool isAPIFunction(const std::string &F) const override; + [[nodiscard]] bool isFactoryFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isConsumingFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isAPIFunction(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State - getNextState(std::string Tok, TypeStateDescription::State S) const override; + getNextState(llvm::StringRef Tok, + TypeStateDescription::State S) const override; [[nodiscard]] TypeStateDescription::State - getNextState(const std::string &Tok, TypeStateDescription::State S, + getNextState(llvm::StringRef Tok, TypeStateDescription::State S, const llvm::CallBase *CallSite) const override; [[nodiscard]] std::string getTypeNameOfInterest() const override; [[nodiscard]] std::set - getConsumerParamIdx(const std::string &F) const override; + getConsumerParamIdx(llvm::StringRef F) const override; [[nodiscard]] std::set - getFactoryParamIdx(const std::string &F) const override; - [[nodiscard]] std::string - stateToString(TypeStateDescription::State S) const override; + getFactoryParamIdx(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State bottom() const override; [[nodiscard]] TypeStateDescription::State top() const override; [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h index 9078d0321..57c41710c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.h @@ -7,8 +7,8 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREMEMORYDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREMEMORYDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREMEMORYDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_OPENSSLSECUREMEMORYDESCRIPTION_H #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" @@ -18,48 +18,30 @@ namespace psr { -class OpenSSLSecureMemoryDescription : public TypeStateDescription { -private: - enum OpenSSLSecureMemoryState { - TOP = 42, - BOT = 0, - ZEROED = 1, - FREED = 2, - ERROR = 3, - ALLOCATED = 4 - }; - - enum class OpenSSLSecureMemoryToken { - CRYPTO_MALLOC = 0, - CRYPTO_ZALLOC = 1, - CRYPTO_FREE = 2, - OPENSSL_CLEANSE = 3, - STAR = 4 - }; - - static const std::map> OpenSSLSecureMemoryFuncs; - // Delta matrix to implement the state machine's Delta function - static const OpenSSLSecureMemoryState Delta[6][7]; - static OpenSSLSecureMemoryToken funcNameToToken(const std::string &F); +enum class OpenSSLSecureMemoryState; +llvm::StringRef to_string(OpenSSLSecureMemoryState State) noexcept; +class OpenSSLSecureMemoryDescription + : public TypeStateDescription { public: - [[nodiscard]] bool isFactoryFunction(const std::string &F) const override; - [[nodiscard]] bool isConsumingFunction(const std::string &F) const override; - [[nodiscard]] bool isAPIFunction(const std::string &F) const override; + using TypeStateDescription::getNextState; + [[nodiscard]] bool isFactoryFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isConsumingFunction(llvm::StringRef F) const override; + [[nodiscard]] bool isAPIFunction(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State - getNextState(std::string Tok, TypeStateDescription::State S) const override; + getNextState(llvm::StringRef Tok, + TypeStateDescription::State S) const override; [[nodiscard]] std::string getTypeNameOfInterest() const override; [[nodiscard]] std::set - getConsumerParamIdx(const std::string &F) const override; + getConsumerParamIdx(llvm::StringRef F) const override; [[nodiscard]] std::set - getFactoryParamIdx(const std::string &F) const override; - [[nodiscard]] std::string - stateToString(TypeStateDescription::State S) const override; + getFactoryParamIdx(llvm::StringRef F) const override; [[nodiscard]] TypeStateDescription::State bottom() const override; [[nodiscard]] TypeStateDescription::State top() const override; [[nodiscard]] TypeStateDescription::State uninit() const override; [[nodiscard]] TypeStateDescription::State start() const override; [[nodiscard]] TypeStateDescription::State error() const override; + [[nodiscard]] DataFlowAnalysisType analysisType() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h index 4302cb0a1..a7e14e8fc 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h @@ -7,8 +7,10 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_TYPESTATEDESCRIPTIONS_TYPESTATEDESCRIPTION_H + +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "llvm/IR/InstrTypes.h" @@ -17,6 +19,20 @@ namespace psr { +struct TypeStateDescriptionBase { + virtual ~TypeStateDescriptionBase() = default; + + [[nodiscard]] virtual bool isFactoryFunction(llvm::StringRef F) const = 0; + [[nodiscard]] virtual bool isConsumingFunction(llvm::StringRef F) const = 0; + [[nodiscard]] virtual bool isAPIFunction(llvm::StringRef F) const = 0; + [[nodiscard]] virtual std::string getTypeNameOfInterest() const = 0; + [[nodiscard]] virtual std::set + getConsumerParamIdx(llvm::StringRef F) const = 0; + [[nodiscard]] virtual std::set + getFactoryParamIdx(llvm::StringRef F) const = 0; + [[nodiscard]] virtual DataFlowAnalysisType analysisType() const = 0; +}; + /** * Interface for a type state problem to be used with the IDETypeStateAnalysis. * It needs to provide a finite state machine to handle state changes and a list @@ -31,31 +47,24 @@ namespace psr { * * @see CSTDFILEIOTypeStateDescription as an example of type state description. */ -struct TypeStateDescription { +template +struct TypeStateDescription : public TypeStateDescriptionBase { /// Type for states of the finite state machine - using State = int; - virtual ~TypeStateDescription() = default; - [[nodiscard]] virtual bool isFactoryFunction(const std::string &F) const = 0; - [[nodiscard]] virtual bool - isConsumingFunction(const std::string &F) const = 0; - [[nodiscard]] virtual bool isAPIFunction(const std::string &F) const = 0; + using State = StateTy; + ~TypeStateDescription() override = default; /** * @brief For a given function name (as a string token) and a state, this * function returns the next state. */ - [[nodiscard]] virtual State getNextState(std::string Tok, State S) const = 0; + [[nodiscard]] virtual State getNextState(llvm::StringRef Tok, + State S) const = 0; [[nodiscard]] virtual State - getNextState(const std::string &Tok, State S, + getNextState(llvm::StringRef Tok, State S, const llvm::CallBase * /*CallSite*/) const { return getNextState(Tok, S); } - [[nodiscard]] virtual std::string getTypeNameOfInterest() const = 0; - [[nodiscard]] virtual std::set - getConsumerParamIdx(const std::string &F) const = 0; - [[nodiscard]] virtual std::set - getFactoryParamIdx(const std::string &F) const = 0; - [[nodiscard]] virtual std::string stateToString(State S) const = 0; + [[nodiscard]] virtual State bottom() const = 0; [[nodiscard]] virtual State top() const = 0; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h index c2ab701a7..df91bce7d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h @@ -7,8 +7,8 @@ * Philipp Schubert, Linus Jungemann, and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOFULLCONSTANTPROPAGATION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOFULLCONSTANTPROPAGATION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOFULLCONSTANTPROPAGATION_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOFULLCONSTANTPROPAGATION_H #include "phasar/DataFlow/Mono/InterMonoProblem.h" #include "phasar/Domain/LatticeDomain.h" @@ -75,12 +75,6 @@ class InterMonoFullConstantPropagation std::unordered_map initialSeeds() override; - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; - void printContainer(llvm::raw_ostream &OS, mono_container_t Con) const override; }; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h index 2c5446829..8674ff564 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOSOLVERTEST_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOSOLVERTEST_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOSOLVERTEST_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOSOLVERTEST_H #include "phasar/DataFlow/Mono/InterMonoProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -77,12 +77,6 @@ class InterMonoSolverTest : public InterMonoProblem { const mono_container_t &In) override; std::unordered_map initialSeeds() override; - - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h index 36bcc8439..211de3a91 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h @@ -14,8 +14,8 @@ * Author: richard leer */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOTAINTANALYSIS_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTERMONOTAINTANALYSIS_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOTAINTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTERMONOTAINTANALYSIS_H #include "phasar/DataFlow/Mono/InterMonoProblem.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" @@ -82,12 +82,6 @@ class InterMonoTaintAnalysis std::unordered_map initialSeeds() override; - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; - [[nodiscard]] const std::map> &getAllLeaks() const; private: diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h index d8b64028e..010e2f517 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h @@ -14,14 +14,15 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOFULLCONSTANTPROPAGATION_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOFULLCONSTANTPROPAGATION_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOFULLCONSTANTPROPAGATION_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOFULLCONSTANTPROPAGATION_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/Utils/BitVectorSet.h" +#include "phasar/Utils/Printer.h" #include #include @@ -44,10 +45,15 @@ class LLVMBasedICFG; class LLVMTypeHierarchy; class InterMonoFullConstantPropagation; +struct IntraMonoFCAFact { + const llvm::Value *Fact{}; + LatticeDomain Value{}; +}; + struct IntraMonoFullConstantPropagationAnalysisDomain : public LLVMAnalysisDomainDefault { using plain_d_t = int64_t; - using d_t = std::pair>; + using d_t = IntraMonoFCAFact; using mono_container_t = std::map>; }; @@ -90,31 +96,23 @@ class IntraMonoFullConstantPropagation const mono_container_t &Rhs) override; std::unordered_map initialSeeds() override; - - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; }; +std::string DToString(const IntraMonoFCAFact &Fact); + } // namespace psr namespace std { -template <> -struct hash>> { - size_t operator()(const std::pair> &P) const { +template <> struct hash { + size_t operator()(const psr::IntraMonoFCAFact &P) const { std::hash HashPtr; - size_t HP = HashPtr(P.first); + size_t HP = HashPtr(P.Fact); size_t HU = 0; // returns nullptr if P.second is Top or Bottom, a valid pointer otherwise if (const auto *Ptr = std::get_if( - &P.second)) { + &P.Value)) { HU = *Ptr; } return HP ^ (HU << 1); diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h index cae80de31..13c5ac42f 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOSOLVERTEST_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOSOLVERTEST_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOSOLVERTEST_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOSOLVERTEST_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -69,13 +69,6 @@ class IntraMonoSolverTest mono_container_t normalFlow(n_t Inst, const mono_container_t &In) override; std::unordered_map initialSeeds() override; - - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, - const llvm::Function *Fun) const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h index c3d4ac5a4..e813e38d3 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOUNINITVARIABLES_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_MONO_PROBLEMS_INTRAMONOUNINITVARIABLES_H +#ifndef PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOUNINITVARIABLES_H +#define PHASAR_PHASARLLVM_DATAFLOW_MONO_PROBLEMS_INTRAMONOUNINITVARIABLES_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" @@ -65,12 +65,6 @@ class IntraMonoUninitVariables mono_container_t normalFlow(n_t Inst, const mono_container_t &In) override; std::unordered_map initialSeeds() override; - - void printNode(llvm::raw_ostream &OS, n_t Inst) const override; - - void printDataFlowFact(llvm::raw_ostream &OS, d_t Fact) const override; - - void printFunction(llvm::raw_ostream &OS, f_t Fun) const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/LLVMPathConstraints.h b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/LLVMPathConstraints.h new file mode 100644 index 000000000..887f4f54b --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/LLVMPathConstraints.h @@ -0,0 +1,97 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_PATHSENSITIVITY_LLVMPATHCONSTRAINTS_H +#define PHASAR_PHASARLLVM_PATHSENSITIVITY_LLVMPATHCONSTRAINTS_H + +#include "phasar/Utils/MaybeUniquePtr.h" + +#include "llvm/ADT/SmallVector.h" + +#include "z3++.h" + +#include + +namespace llvm { +class Value; +class Instruction; +class AllocaInst; +class LoadInst; +class GetElementPtrInst; +class PHINode; +class BranchInst; +class CmpInst; +class BinaryOperator; +class CallBase; +} // namespace llvm + +namespace psr { +class LLVMPathConstraints { +public: + struct ConstraintAndVariables { + z3::expr Constraint; + llvm::SmallVector Variables; + }; + + explicit LLVMPathConstraints(z3::context *Z3Ctx = nullptr, + bool IgnoreDebugInstructions = true); + + z3::context &getContext() noexcept { return *Z3Ctx; } + const z3::context &getContext() const noexcept { return *Z3Ctx; } + + std::optional getConstraintFromEdge(const llvm::Instruction *Curr, + const llvm::Instruction *Succ); + + std::optional + getConstraintAndVariablesFromEdge(const llvm::Instruction *Curr, + const llvm::Instruction *Succ); + +private: + [[nodiscard]] std::optional + internalGetConstraintAndVariablesFromEdge(const llvm::Instruction *From, + const llvm::Instruction *To); + + /// Allocas are the most basic building blocks and represent a leaf value. + [[nodiscard]] ConstraintAndVariables + getAllocaInstAsZ3(const llvm::AllocaInst *Alloca); + + /// Load instrutions may also represent leafs. + [[nodiscard]] ConstraintAndVariables + getLoadInstAsZ3(const llvm::LoadInst *Load); + + /// GEP instructions may also represent leafs. + [[nodiscard]] ConstraintAndVariables + getGEPInstAsZ3(const llvm::GetElementPtrInst *GEP); + + [[nodiscard]] ConstraintAndVariables + handlePhiInstruction(const llvm::PHINode *Phi); + + [[nodiscard]] ConstraintAndVariables + handleCondBrInst(const llvm::BranchInst *Br, const llvm::Instruction *Succ); + + [[nodiscard]] ConstraintAndVariables handleCmpInst(const llvm::CmpInst *Cmp); + + [[nodiscard]] ConstraintAndVariables + handleBinaryOperator(const llvm::BinaryOperator *BinOp); + + [[nodiscard]] ConstraintAndVariables getLiteralAsZ3(const llvm::Value *V); + + [[nodiscard]] ConstraintAndVariables + getFunctionCallAsZ3(const llvm::CallBase *CallSite); + + friend class SizeGuardCheck; + friend class LoopGuardCheck; + + MaybeUniquePtr Z3Ctx; + std::unordered_map Z3Expr; + bool IgnoreDebugInstructions; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_PATHSENSITIVITY_LLVMPATHCONSTRAINTS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitivityConfig.h b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitivityConfig.h new file mode 100644 index 000000000..3d219f5fc --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitivityConfig.h @@ -0,0 +1,41 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYCONFIG_H +#define PHASAR_PHASARLLVM_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYCONFIG_H + +#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h" + +#include "z3++.h" + +#include + +namespace psr { +struct Z3BasedPathSensitivityConfig + : PathSensitivityConfigBase { + std::optional AdditionalConstraint; + + [[nodiscard]] Z3BasedPathSensitivityConfig + withAdditionalConstraint(const z3::expr &Constr) const &noexcept { + auto Ret = *this; + Ret.AdditionalConstraint = + Ret.AdditionalConstraint ? *Ret.AdditionalConstraint && Constr : Constr; + return Ret; + } + + [[nodiscard]] Z3BasedPathSensitivityConfig + withAdditionalConstraint(const z3::expr &Constr) &&noexcept { + AdditionalConstraint = + AdditionalConstraint ? *AdditionalConstraint && Constr : Constr; + return std::move(*this); + } +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYCONFIG_H diff --git a/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h new file mode 100644 index 000000000..988a977c4 --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitvityManager.h @@ -0,0 +1,186 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * 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_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYMANAGER_H +#define PHASAR_PHASARLLVM_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYMANAGER_H + +#include "phasar/DataFlow/PathSensitivity/FlowPath.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h" +#include "phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h" +#include "phasar/PhasarLLVM/DataFlow/PathSensitivity/Z3BasedPathSensitivityConfig.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/MaybeUniquePtr.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include "z3++.h" + +#include +#include +#include +#include +#include + +namespace llvm { +class Instruction; +} // namespace llvm + +namespace psr { +class LLVMPathConstraints; + +class Z3BasedPathSensitivityManagerBase + : public PathSensitivityManagerBase { +public: + using n_t = const llvm::Instruction *; + + static_assert(is_removable_graph_trait_v, + "Invalid graph type: Must support edge-removal!"); + +protected: + z3::expr filterOutUnreachableNodes(graph_type &RevDAG, vertex_t Leaf, + const Z3BasedPathSensitivityConfig &Config, + LLVMPathConstraints &LPC) const; + + FlowPathSequence + filterAndFlattenRevDag(graph_type &RevDAG, vertex_t Leaf, n_t FinalInst, + const Z3BasedPathSensitivityConfig &Config, + LLVMPathConstraints &LPC) const; + + static void deduplicatePaths(FlowPathSequence &Paths); +}; + +template >> +class Z3BasedPathSensitivityManager + : public Z3BasedPathSensitivityManagerBase, + public PathSensitivityManagerMixin< + Z3BasedPathSensitivityManager, AnalysisDomainTy, + typename Z3BasedPathSensitivityManagerBase::graph_type> { + using base_t = PathSensitivityManagerBase; + using mixin_t = PathSensitivityManagerMixin; + +public: + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using typename PathSensitivityManagerBase::graph_type; + using MaybeFlowPathSeq = std::variant, z3::expr>; + + explicit Z3BasedPathSensitivityManager( + const ExplodedSuperGraph *ESG, + Z3BasedPathSensitivityConfig Config, LLVMPathConstraints *LPC = nullptr) + : mixin_t(ESG), Config(std::move(Config)), LPC(LPC) { + if (!LPC) { + this->LPC = std::make_unique(); + } + } + + FlowPathSequence pathsTo(n_t Inst, d_t Fact) const { + if (Config.DAGSizeThreshold != SIZE_MAX) { + PHASAR_LOG_LEVEL( + WARNING, + "Attempting to compute FlowPaths without conditionally " + "falling back to constraint collection, but a DAGSizeThreshold " + "is specified. It will be ignored here. To make use of it, " + "please call the pathsOrConstraintTo function instead!"); + } + + graph_type Dag = this->pathsDagTo(Inst, std::move(Fact), Config); + + PHASAR_LOG_LEVEL_CAT( + DEBUG, "PathSensitivityManager", + "PathsTo with MaxDAGDepth: " << Config.DAGDepthThreshold); + +#ifndef NDEBUG + { + std::error_code EC; + llvm::raw_fd_stream ROS( + "dag-" + + std::filesystem::path(psr::getFilePathFromIR(Inst)) + .filename() + .string() + + "-" + psr::getMetaDataID(Inst) + ".dot", + EC); + assert(!EC); + printGraph(Dag, ROS, "DAG", [](llvm::ArrayRef PartialPath) { + std::string Buf; + llvm::raw_string_ostream ROS(Buf); + ROS << "[ "; + llvm::interleaveComma(PartialPath, ROS, [&ROS](const auto *Inst) { + ROS << psr::getMetaDataID(Inst); + }); + ROS << " ]"; + ROS.flush(); + return Buf; + }); + + llvm::errs() << "Paths DAG has " << Dag.Roots.size() << " roots\n"; + } +#endif + + vertex_t Leaf = [&Dag] { + for (auto Vtx : graph_traits_t::vertices(Dag)) { + if (graph_traits_t::outDegree(Dag, Vtx) == 0) { + return Vtx; + } + } + llvm_unreachable("Expect the DAG to have a leaf node!"); + }(); + + z3::expr Constraint = filterOutUnreachableNodes(Dag, Leaf, Config, *LPC); + + if (Constraint.is_false()) { + PHASAR_LOG_LEVEL_CAT(INFO, "PathSensitivityManager", + "The query position is unreachable"); + return FlowPathSequence(); + } + + auto Ret = filterAndFlattenRevDag(Dag, Leaf, Inst, Config, *LPC); + + deduplicatePaths(Ret); + +#ifndef NDEBUG +#ifdef DYNAMIC_LOG + PHASAR_LOG_LEVEL_CAT(DEBUG, "PathSensitivityManager", + "Recorded " << Ret.size() << " valid paths:"); + + std::string Str; + for (const FlowPath &Path : Ret) { + Str.clear(); + llvm::raw_string_ostream ROS(Str); + ROS << "> "; + llvm::interleaveComma(Path.Path, ROS, + [&ROS](auto *Inst) { ROS << getMetaDataID(Inst); }); + ROS << ": " << Path.Constraint.to_string(); + ROS.flush(); + PHASAR_LOG_LEVEL_CAT(DEBUG, "PathSensitivityManager", Str); + } + +#endif // DYNAMIC_LOG +#endif // NDEBUG + + return Ret; + } + +private: + Z3BasedPathSensitivityConfig Config{}; + /// FIXME: Not using 'mutable' here + mutable MaybeUniquePtr LPC{}; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_PATHSENSITIVITY_Z3BASEDPATHSENSITIVITYMANAGER_H diff --git a/include/phasar/PhasarLLVM/Domain.h b/include/phasar/PhasarLLVM/Domain.h new file mode 100644 index 000000000..db363c242 --- /dev/null +++ b/include/phasar/PhasarLLVM/Domain.h @@ -0,0 +1,15 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_DOMAIN_H +#define PHASAR_PHASARLLVM_DOMAIN_H + +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" + +#endif // PHASAR_PHASARLLVM_DOMAIN_H diff --git a/include/phasar/PhasarLLVM/HelperAnalyses.h b/include/phasar/PhasarLLVM/HelperAnalyses.h index 07b09878d..f8cb8029b 100644 --- a/include/phasar/PhasarLLVM/HelperAnalyses.h +++ b/include/phasar/PhasarLLVM/HelperAnalyses.h @@ -7,8 +7,8 @@ * Fabian Schiebel, Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSES_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSES_H_ +#ifndef PHASAR_PHASARLLVM_HELPERANALYSES_H +#define PHASAR_PHASARLLVM_HELPERANALYSES_H #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/PhasarLLVM/HelperAnalysisConfig.h" @@ -20,6 +20,10 @@ #include #include +namespace llvm { +class Module; +} // namespace llvm + namespace psr { class LLVMProjectIRDB; class LLVMTypeHierarchy; @@ -33,6 +37,7 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) std::optional PrecomputedPTS, AliasAnalysisType PTATy, bool AllowLazyPTS, std::vector EntryPoints, + std::optional PrecomputedCG, CallGraphAnalysisType CGTy, Soundness SoundnessLevel, bool AutoGlobalSupport) noexcept; @@ -45,6 +50,12 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) explicit HelperAnalyses(const char *IRFile, std::vector EntryPoints, HelperAnalysisConfig Config = {}); + explicit HelperAnalyses(llvm::Module *IRModule, + std::vector EntryPoints, + HelperAnalysisConfig Config = {}); + explicit HelperAnalyses(std::unique_ptr IRModule, + std::vector EntryPoints, + HelperAnalysisConfig Config = {}); ~HelperAnalyses() noexcept; [[nodiscard]] LLVMProjectIRDB &getProjectIRDB(); @@ -69,6 +80,7 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) bool AllowLazyPTS{}; // ICF + std::optional PrecomputedCG; std::vector EntryPoints; CallGraphAnalysisType CGTy{}; Soundness SoundnessLevel{}; @@ -76,4 +88,4 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) }; } // namespace psr -#endif // PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSES_H_ +#endif // PHASAR_PHASARLLVM_HELPERANALYSES_H diff --git a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h index d35195e76..df59c41c5 100644 --- a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h +++ b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSISCONFIG_H -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSISCONFIG_H +#ifndef PHASAR_PHASARLLVM_HELPERANALYSISCONFIG_H +#define PHASAR_PHASARLLVM_HELPERANALYSISCONFIG_H #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/Pointer/AliasAnalysisType.h" @@ -21,11 +21,15 @@ namespace psr { struct HelperAnalysisConfig { std::optional PrecomputedPTS = std::nullopt; + std::optional PrecomputedCG = std::nullopt; AliasAnalysisType PTATy = AliasAnalysisType::CFLAnders; CallGraphAnalysisType CGTy = CallGraphAnalysisType::OTF; Soundness SoundnessLevel = Soundness::Soundy; bool AutoGlobalSupport = true; bool AllowLazyPTS = true; + /// Preprocess a ProjectIRDB even if it gets constructed by an already + /// existing llvm::Module + bool PreprocessExistingModule = true; HelperAnalysisConfig &&withCGType(CallGraphAnalysisType CGTy) &&noexcept { this->CGTy = CGTy; @@ -34,4 +38,4 @@ struct HelperAnalysisConfig { }; } // namespace psr -#endif // PHASAR_PHASARLLVM_ANALYSISSTRATEGY_HELPERANALYSISCONFIG_H +#endif // PHASAR_PHASARLLVM_HELPERANALYSISCONFIG_H diff --git a/include/phasar/PhasarLLVM/Passes.h b/include/phasar/PhasarLLVM/Passes.h new file mode 100644 index 000000000..9c65ea033 --- /dev/null +++ b/include/phasar/PhasarLLVM/Passes.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_PASSES_H +#define PHASAR_PHASARLLVM_PASSES_H + +#include "phasar/PhasarLLVM/Passes/ExampleModulePass.h" +#include "phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h" +#include "phasar/PhasarLLVM/Passes/ValueAnnotationPass.h" + +#endif // PHASAR_PHASARLLVM_PASSES_H diff --git a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h index ca6767a3a..a33b6e10d 100644 --- a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h +++ b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h @@ -33,107 +33,156 @@ class Module; namespace psr { -class GeneralStatistics { -private: - friend class GeneralStatisticsAnalysis; +struct GeneralStatistics { + size_t Functions = 0; + size_t ExternalFunctions = 0; + size_t FunctionDefinitions = 0; + size_t AddressTakenFunctions = 0; size_t Globals = 0; + size_t GlobalConsts = 0; + size_t ExternalGlobals = 0; + size_t GlobalsDefinitions = 0; size_t BasicBlocks = 0; size_t AllocationSites = 0; size_t CallSites = 0; + size_t DebugIntrinsics = 0; size_t Instructions = 0; size_t StoreInstructions = 0; size_t LoadInstructions = 0; size_t MemIntrinsics = 0; - size_t GlobalPointers = 0; size_t Branches = 0; + size_t Switches = 0; size_t GetElementPtrs = 0; + size_t LandingPads = 0; size_t PhiNodes = 0; - size_t GlobalConsts = 0; + size_t NumInlineAsm = 0; + size_t IndCalls = 0; + size_t TotalNumOperands = 0; + size_t TotalNumUses = 0; + size_t TotalNumPredecessorBBs = 0; + size_t TotalNumSuccessorBBs = 0; + size_t MaxNumOperands = 0; + size_t MaxNumUses = 0; + size_t MaxNumPredecessorBBs = 0; + size_t MaxNumSuccessorBBs = 0; + size_t NumInstWithMultipleUses = 0; + size_t NumInstsUsedOutsideBB = 0; + size_t NonVoidInsts = 0; std::set AllocatedTypes; std::set AllocaInstructions; std::set RetResInstructions; - std::string ModuleName = ""; + std::string ModuleName{}; -public: /** * @brief Returns the number of Allocation sites. */ - [[nodiscard]] size_t getAllocationsites() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use AllocationSites instead")]] size_t + getAllocationsites() const; /** * @brief Returns the number of Function calls. */ - [[nodiscard]] size_t getFunctioncalls() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use CallSites instead")]] size_t + getFunctioncalls() const; /** * @brief Returns the number of Instructions. */ - [[nodiscard]] size_t getInstructions() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use Instructions instead")]] size_t + getInstructions() const; /** * @brief Returns the number of global pointers. */ - [[nodiscard]] size_t getGlobalPointers() const; + [[nodiscard]] [[deprecated( + "All globals are pointers. Use Globals instead")]] size_t + getGlobalPointers() const; /** * @brief Returns the number of basic blocks. */ - [[nodiscard]] size_t getBasicBlocks() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use BasicBlocks instead")]] size_t + getBasicBlocks() const; /** * @brief Returns the number of functions. */ - [[nodiscard]] size_t getFunctions() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use Functions instead")]] size_t + getFunctions() const; /** * @brief Returns the number of globals. */ - [[nodiscard]] size_t getGlobals() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use Globals instead")]] size_t + getGlobals() const; /** * @brief Returns the number of constant globals. */ - [[nodiscard]] size_t getGlobalConsts() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use GlobalConsts instead")]] size_t + getGlobalConsts() const; /** * @brief Returns the number of memory intrinsics. */ - [[nodiscard]] size_t getMemoryIntrinsics() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use MemIntrinsics instead")]] size_t + getMemoryIntrinsics() const; /** * @brief Returns the number of store instructions. */ - [[nodiscard]] size_t getStoreInstructions() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use StoreInstructions instead")]] size_t + getStoreInstructions() const; /** * @brief Returns the number of load instructions. */ - [[nodiscard]] size_t getLoadInstructions(); + [[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]] const std::set &getAllocatedTypes() const; + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use AllocatedTypes instead")]] const std:: + set & + getAllocatedTypes() const; /** * @brief Returns all stack and heap allocating instructions. */ - [[nodiscard]] const std::set & + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use AllocaInstructions " + "instead")]] const std::set & getAllocaInstructions() const; /** * @brief Returns all Return and Resume Instructions. */ - [[nodiscard]] const std::set & + [[nodiscard]] [[deprecated( + "Getters are no longer needed. Use RetResInstructions " + "instead")]] const std::set & getRetResInstructions() const; + [[nodiscard]] nlohmann::json getAsJson() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; - - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const GeneralStatistics &Statistics); }; +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const GeneralStatistics &Statistics); + /** * This class uses the Module Pass Mechanism of LLVM to compute * some statistics about a Module. This includes the number of diff --git a/include/phasar/PhasarLLVM/Pointer.h b/include/phasar/PhasarLLVM/Pointer.h new file mode 100644 index 000000000..220567251 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_H +#define PHASAR_PHASARLLVM_POINTER_H + +#include "phasar/PhasarLLVM/Pointer/LLVMAliasGraph.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/LLVMBasedAliasAnalysis.h b/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h index e724a0c4d..c37cfda21 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h @@ -13,8 +13,6 @@ #include "phasar/Pointer/AliasAnalysisType.h" #include "llvm/Analysis/AliasAnalysis.h" -#include "llvm/IR/PassManager.h" -#include "llvm/Passes/PassBuilder.h" namespace llvm { class Value; @@ -27,23 +25,19 @@ namespace psr { class LLVMProjectIRDB; class LLVMBasedAliasAnalysis { -private: - llvm::PassBuilder PB; - llvm::AAManager AA; - llvm::FunctionAnalysisManager FAM; - llvm::FunctionPassManager FPM; - llvm::DenseMap AAInfos; - AliasAnalysisType PATy; - - [[nodiscard]] bool hasAliasInfo(const llvm::Function &Fun) const; - - void computeAliasInfo(llvm::Function &Fun); public: - LLVMBasedAliasAnalysis(LLVMProjectIRDB &IRDB, bool UseLazyEvaluation = true, - AliasAnalysisType PATy = AliasAnalysisType::CFLAnders); + explicit LLVMBasedAliasAnalysis( + LLVMProjectIRDB &IRDB, bool UseLazyEvaluation, + AliasAnalysisType PATy = AliasAnalysisType::Basic); - ~LLVMBasedAliasAnalysis() = default; + LLVMBasedAliasAnalysis(LLVMBasedAliasAnalysis &&) noexcept = default; + LLVMBasedAliasAnalysis & + operator=(LLVMBasedAliasAnalysis &&) noexcept = default; + + LLVMBasedAliasAnalysis(const LLVMBasedAliasAnalysis &) = delete; + LLVMBasedAliasAnalysis &operator=(const LLVMBasedAliasAnalysis &) = delete; + ~LLVMBasedAliasAnalysis(); void print(llvm::raw_ostream &OS = llvm::outs()) const; @@ -54,13 +48,26 @@ class LLVMBasedAliasAnalysis { return AAInfos.lookup(F); }; - void erase(llvm::Function *F); + void erase(llvm::Function *F) noexcept; - void clear(); + void clear() noexcept; - [[nodiscard]] inline AliasAnalysisType getPointerAnalysisType() const { + [[nodiscard]] inline AliasAnalysisType + getPointerAnalysisType() const noexcept { return PATy; }; + +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::DenseMap AAInfos; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h b/include/phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h index 6ac25f29f..c1af0c3f2 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMPointsToUtils.h @@ -10,7 +10,6 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMPOINTSTOUTILS_H_ #define PHASAR_PHASARLLVM_POINTER_LLVMPOINTSTOUTILS_H_ -#include "llvm/ADT/StringRef.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Value.h" @@ -29,8 +28,6 @@ namespace psr { !llvm::isa(V); } -[[nodiscard]] bool isHeapAllocatingFunction(const llvm::Function *Fun); - } // namespace psr #endif diff --git a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h index 1931705cf..17efc1abb 100644 --- a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h +++ b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h @@ -22,12 +22,15 @@ #include "boost/graph/adjacency_list.hpp" #include "boost/graph/graph_traits.hpp" #include "boost/graph/reverse_graph.hpp" -#include "gtest/gtest_prod.h" #include #include #include +#ifndef PSR_FRIEND_TEST +#define PSR_FRIEND_TEST(TEST, CLASS) +#endif + namespace llvm { class StructType; } // namespace llvm @@ -72,13 +75,13 @@ class CachedTypeGraph : public TypeGraph { graph_t G; bool AlreadyVisited = false; - FRIEND_TEST(TypeGraphTest, AddType); - FRIEND_TEST(TypeGraphTest, AddLinkSimple); - FRIEND_TEST(TypeGraphTest, AddLinkWithSubs); - FRIEND_TEST(TypeGraphTest, AddLinkWithRecursion); - FRIEND_TEST(TypeGraphTest, ReverseTypePropagation); - FRIEND_TEST(TypeGraphTest, TypeAggregation); - FRIEND_TEST(TypeGraphTest, Merging); + 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); diff --git a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h index 025a4ffd6..a29e6a45e 100644 --- a/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h +++ b/include/phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h @@ -22,7 +22,6 @@ #include "boost/graph/adjacency_list.hpp" #include "boost/graph/graph_traits.hpp" #include "boost/graph/reverse_graph.hpp" -#include "gtest/gtest_prod.h" #include #include diff --git a/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h b/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h index 7976a42ac..fce91ea9c 100644 --- a/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h +++ b/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h @@ -7,8 +7,8 @@ * Fabian Schiebel, and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_ANALYSISSTRATEGY_SIMPLEANALYSISCONSTRUCTOR_H_ -#define PHASAR_PHASARLLVM_ANALYSISSTRATEGY_SIMPLEANALYSISCONSTRUCTOR_H_ +#ifndef PHASAR_PHASARLLVM_SIMPLEANALYSISCONSTRUCTOR_H +#define PHASAR_PHASARLLVM_SIMPLEANALYSISCONSTRUCTOR_H #include "phasar/PhasarLLVM/HelperAnalyses.h" @@ -66,4 +66,4 @@ ProblemTy createAnalysisProblem(HelperAnalyses &HA, ArgTys &&...Args) { } // namespace psr -#endif // PHASAR_PHASARLLVM_ANALYSISSTRATEGY_SIMPLEANALYSISCONSTRUCTOR_H_ +#endif // PHASAR_PHASARLLVM_SIMPLEANALYSISCONSTRUCTOR_H diff --git a/include/phasar/PhasarLLVM/TaintConfig.h b/include/phasar/PhasarLLVM/TaintConfig.h new file mode 100644 index 000000000..5335aaf59 --- /dev/null +++ b/include/phasar/PhasarLLVM/TaintConfig.h @@ -0,0 +1,17 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_TAINTCONFIG_H +#define PHASAR_PHASARLLVM_TAINTCONFIG_H + +#include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h" +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h" + +#endif // PHASAR_PHASARLLVM_TAINTCONFIG_H diff --git a/include/phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h b/include/phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h index 04c11650f..31115da09 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h +++ b/include/phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h @@ -19,6 +19,7 @@ namespace psr { class LLVMTaintConfig; class LLVMProjectIRDB; +struct TaintConfigData; template <> struct TaintConfigTraits { using n_t = const llvm::Instruction *; @@ -31,7 +32,7 @@ class LLVMTaintConfig : public TaintConfigBase { public: explicit LLVMTaintConfig(const psr::LLVMProjectIRDB &Code, - const nlohmann::json &Config); + const psr::TaintConfigData &Config); explicit LLVMTaintConfig(const psr::LLVMProjectIRDB &AnnotatedCode); explicit LLVMTaintConfig( TaintDescriptionCallBackTy SourceCB, TaintDescriptionCallBackTy SinkCB, @@ -93,7 +94,7 @@ class LLVMTaintConfig : public TaintConfigBase { // --- utilities void addAllFunctions(const LLVMProjectIRDB &IRDB, - const nlohmann::json &Config); + const TaintConfigData &Config); // --- data members diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h index 94979d2af..422a06a5b 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigBase.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGBASE_H #define PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGBASE_H +#include "phasar/PhasarLLVM/TaintConfig/TaintConfigData.h" #include "phasar/Utils/Nullable.h" #include "llvm/ADT/FunctionExtras.h" @@ -17,8 +18,6 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include #include #include @@ -26,7 +25,7 @@ namespace psr { -enum class TaintCategory { Source, Sink, Sanitizer, None }; +enum class TaintCategory { None, Source, Sink, Sanitizer }; [[nodiscard]] llvm::StringRef to_string(TaintCategory Cat) noexcept; [[nodiscard]] TaintCategory toTaintCategory(llvm::StringRef Str) noexcept; @@ -159,8 +158,9 @@ template class TaintConfigBase { //===----------------------------------------------------------------------===// // Miscellaneous helper functions -nlohmann::json parseTaintConfig(const llvm::Twine &Path); -std::optional parseTaintConfigOrNull(const llvm::Twine &Path); +[[nodiscard]] TaintConfigData parseTaintConfig(const llvm::Twine &Path); +[[nodiscard]] std::optional +parseTaintConfigOrNull(const llvm::Twine &Path) noexcept; } // namespace psr diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h new file mode 100644 index 000000000..ba92829e5 --- /dev/null +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h @@ -0,0 +1,47 @@ +/****************************************************************************** + * Copyright (c) 2023 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGDATA_H +#define PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGDATA_H + +#include +#include +#include + +namespace psr { +enum class TaintCategory; + +struct FunctionData { + FunctionData() noexcept = default; + + std::string Name; + TaintCategory ReturnCat{}; + std::vector SourceValues; + std::vector SinkValues; + std::vector SanitizerValues; + bool HasAllSinkParam = false; +}; + +struct VariableData { + VariableData() noexcept = default; + + size_t Line{}; + std::string Name; + std::string Scope; + TaintCategory Cat{}; +}; + +struct TaintConfigData { + std::vector Functions; + std::vector Variables; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGDATA_H diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h index 502b55989..aa7941deb 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigUtilities.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_TAINTCONFIG_LLVMTAINTCONFIGUTILITIES_H -#define PHASAR_PHASARLLVM_TAINTCONFIG_LLVMTAINTCONFIGUTILITIES_H +#ifndef PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGUTILITIES_H +#define PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGUTILITIES_H #include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" @@ -87,4 +87,4 @@ void collectSanitizedFacts(ContainerTy &Dest, const LLVMTaintConfig &Config, } } // namespace psr -#endif // PHASAR_PHASARLLVM_TAINTCONFIG_LLVMTAINTCONFIGUTILITIES_H +#endif // PHASAR_PHASARLLVM_TAINTCONFIG_TAINTCONFIGUTILITIES_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy.h new file mode 100644 index 000000000..a8a9b4d12 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_TYPEHIERARCHY_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_H + +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h new file mode 100644 index 000000000..0048e2ecf --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h @@ -0,0 +1,125 @@ +/****************************************************************************** + * Copyright (c) 2023 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_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H + +#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 + +namespace psr { +class LLVMProjectIRDB; + +class DIBasedTypeHierarchy + : public TypeHierarchy { +public: + using ClassType = const llvm::DIType *; + using f_t = const llvm::Function *; + + explicit DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB); + ~DIBasedTypeHierarchy() override = default; + + [[nodiscard]] bool hasType(ClassType Type) const override { + return TypeToVertex.count(Type); + } + + [[nodiscard]] bool isSubType(ClassType Type, ClassType SubType) override { + return llvm::is_contained(subTypesOf(Type), SubType); + } + + [[nodiscard]] std::set getSubTypes(ClassType Type) override { + const auto &Range = subTypesOf(Type); + return {Range.begin(), Range.end()}; + } + + /// A more efficient version of getSubTypes() + [[nodiscard]] llvm::iterator_range + subTypesOf(ClassType Ty) const noexcept; + + [[nodiscard]] bool isSuperType(ClassType Type, ClassType SuperType) override; + + /// Not supported yet + [[nodiscard]] std::set getSuperTypes(ClassType Type) override; + + [[nodiscard]] ClassType + getType(std::string TypeName) const noexcept override { + return NameToType.lookup(TypeName); + } + + [[nodiscard]] std::set getAllTypes() const override { + return {VertexTypes.begin(), VertexTypes.end()}; + } + + [[nodiscard]] const auto &getAllVTables() const noexcept { return VTables; } + + [[nodiscard]] std::string getTypeName(ClassType Type) const override { + return Type->getName().str(); + } + + [[nodiscard]] bool hasVFTable(ClassType Type) const override; + + [[nodiscard]] const VFTable *getVFTable(ClassType Type) const override { + auto It = TypeToVertex.find(Type); + if (It == TypeToVertex.end()) { + return nullptr; + } + return &VTables[It->second]; + } + + [[nodiscard]] size_t size() const override { return VertexTypes.size(); } + + [[nodiscard]] bool empty() const override { return VertexTypes.empty(); } + + void print(llvm::raw_ostream &OS = llvm::outs()) const override; + + /** + * @brief Prints the class hierarchy to an ostream in dot format. + * @param OS outputstream + */ + void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; + + [[nodiscard]] nlohmann::json getAsJson() const override; + +private: + [[nodiscard]] llvm::iterator_range + subTypesOf(size_t TypeIdx) const noexcept; + + // --- + + llvm::StringMap NameToType; + // Map each type to an integer index that is used by VertexTypes and + // DerivedTypesOf. + // Note: all the below arrays should always have the same size (except for + // Hierarchy)! + llvm::DenseMap TypeToVertex; + // The class types we care about ("VertexProperties") + std::vector VertexTypes; + std::vector> TransitiveDerivedIndex; + // The inheritance graph linearized as-if constructed by L2R pre-order + // traversal from the roots. Allows efficient access to the transitive closure + // without ever storing it explicitly. This only works, because the type-graph + // is known to never contain loops + std::vector Hierarchy; + + // The VTables of the polymorphic types in the TH. default-constructed if not + // exists + std::deque VTables; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h index 6024792d8..c5b744e12 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h @@ -24,8 +24,6 @@ #include "boost/graph/adjacency_list.hpp" #include "boost/graph/graph_traits.hpp" -#include "gtest/gtest_prod.h" -#include "nlohmann/json.hpp" #include #include @@ -132,10 +130,6 @@ class LLVMTypeHierarchy std::vector getVirtualFunctions(const llvm::Module &M, const llvm::StructType &Type); - // FRIEND_TEST(VTableTest, SameTypeDifferentVTables); - FRIEND_TEST(LTHTest, GraphConstruction); - FRIEND_TEST(LTHTest, HandleLoadAndPrintOfNonEmptyGraph); - protected: void buildLLVMTypeHierarchy(const llvm::Module &M); diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h index 2feb38f3a..fd55a006a 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h @@ -32,11 +32,12 @@ namespace psr { class LLVMVFTable : public VFTable { private: friend class LLVMTypeHierarchy; + friend class DIBasedTypeHierarchy; std::vector VFT; - LLVMVFTable(std::vector Fs) : VFT(std::move(Fs)) {} public: LLVMVFTable() = default; + LLVMVFTable(std::vector Fs) : VFT(std::move(Fs)) {} ~LLVMVFTable() override = default; /** diff --git a/include/phasar/PhasarLLVM/Utils.h b/include/phasar/PhasarLLVM/Utils.h new file mode 100644 index 000000000..cda1b77dc --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils.h @@ -0,0 +1,21 @@ +/****************************************************************************** + * Copyright (c) 2023 Philipp Schubert. + * 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_UTILS_H +#define PHASAR_PHASARLLVM_UTILS_H + +#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" + +#endif // PHASAR_PHASARLLVM_UTILS_H diff --git a/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h b/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h index 664f28304..4ec3570b5 100644 --- a/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h +++ b/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h @@ -12,6 +12,7 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FunctionExtras.h" +#include "llvm/IR/Dominators.h" #include #include diff --git a/include/phasar/PhasarLLVM/Utils/LLVMCXXShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMCXXShorthands.h index b4b3449f9..e5be26d7c 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMCXXShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMCXXShorthands.h @@ -1,5 +1,5 @@ -#ifndef PHASAR_UTILS_LLVMCXXSHORTHANDS_H -#define PHASAR_UTILS_LLVMCXXSHORTHANDS_H +#ifndef PHASAR_PHASARLLVM_UTILS_LLVMCXXSHORTHANDS_H +#define PHASAR_PHASARLLVM_UTILS_LLVMCXXSHORTHANDS_H // This contains LLVM IR Shorthands specific to C++ // See // https://mapping-high-level-constructs-to-llvm-ir.readthedocs.io/en/latest/object-oriented-constructs/classes.html diff --git a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h index 1c6013f61..9432e9c92 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h @@ -14,11 +14,12 @@ * Author: rleer */ -#ifndef PHASAR_UTILS_LLVMIRTOSRC_H_ -#define PHASAR_UTILS_LLVMIRTOSRC_H_ +#ifndef PHASAR_PHASARLLVM_UTILS_LLVMIRTOSRC_H +#define PHASAR_PHASARLLVM_UTILS_LLVMIRTOSRC_H #include "nlohmann/json.hpp" +#include #include // Forward declaration of types for which we only use its pointer or ref type @@ -29,25 +30,33 @@ class Function; class Value; class GlobalVariable; class Module; +class DIFile; } // namespace llvm namespace psr { -std::string getVarNameFromIR(const llvm::Value *V); +[[nodiscard]] std::string getVarNameFromIR(const llvm::Value *V); -std::string getFunctionNameFromIR(const llvm::Value *V); +[[nodiscard]] std::string getFunctionNameFromIR(const llvm::Value *V); -std::string getFilePathFromIR(const llvm::Value *V); +[[nodiscard]] std::string getFilePathFromIR(const llvm::Value *V); +[[nodiscard]] std::string getFilePathFromIR(const llvm::DIFile *DIF); -std::string getDirectoryFromIR(const llvm::Value *V); +[[nodiscard]] std::string getDirectoryFromIR(const llvm::Value *V); -unsigned int getLineFromIR(const llvm::Value *V); +[[nodiscard]] const llvm::DIFile *getDIFileFromIR(const llvm::Value *V); -unsigned int getColumnFromIR(const llvm::Value *V); +[[nodiscard]] unsigned int getLineFromIR(const llvm::Value *V); -std::string getSrcCodeFromIR(const llvm::Value *V); +[[nodiscard]] unsigned int getColumnFromIR(const llvm::Value *V); -std::string getModuleIDFromIR(const llvm::Value *V); +[[nodiscard]] std::pair +getLineAndColFromIR(const llvm::Value *V); + +[[nodiscard]] std::string getSrcCodeFromIR(const llvm::Value *V, + bool Trim = true); + +[[nodiscard]] std::string getModuleIDFromIR(const llvm::Value *V); struct SourceCodeInfo { std::string SourceCodeLine; @@ -76,7 +85,16 @@ void from_json(const nlohmann::json &J, SourceCodeInfo &Info); /// SourceCodeInfo void to_json(nlohmann::json &J, const SourceCodeInfo &Info); -SourceCodeInfo getSrcCodeInfoFromIR(const llvm::Value *V); +[[nodiscard]] SourceCodeInfo getSrcCodeInfoFromIR(const llvm::Value *V); + +struct DebugLocation { + unsigned Line{}; + unsigned Column{}; + const llvm::DIFile *File{}; +}; + +[[nodiscard]] std::optional +getDebugLocation(const llvm::Value *V); } // namespace psr diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index 5639c7a17..11342497c 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -14,8 +14,8 @@ * Author: philipp */ -#ifndef PHASAR_UTILS_LLVMSHORTHANDS_H_ -#define PHASAR_UTILS_LLVMSHORTHANDS_H_ +#ifndef PHASAR_PHASARLLVM_UTILS_LLVMSHORTHANDS_H +#define PHASAR_PHASARLLVM_UTILS_LLVMSHORTHANDS_H #include "phasar/Utils/Utilities.h" @@ -61,6 +61,7 @@ bool isIntegerLikeType(const llvm::Type *T) noexcept; * heap allocation function, e.g. new, new[], malloc, realloc or calloc. */ bool isAllocaInstOrHeapAllocaFunction(const llvm::Value *V) noexcept; +bool isHeapAllocatingFunction(const llvm::Function *F) noexcept; // TODO add description bool matchesSignature(const llvm::Function *F, const llvm::FunctionType *FType, diff --git a/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h b/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h new file mode 100644 index 000000000..1828c6e53 --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils/LLVMSourceManager.h @@ -0,0 +1,36 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H +#define PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/SourceMgr.h" + +#include + +namespace llvm { +class Value; +class DIFile; +} // namespace llvm + +namespace psr { +struct ManagedDebugLocation { + unsigned Line{}; + unsigned Column{}; + unsigned File{}; +}; + +class LLVMSourceManager { +public: + [[nodiscard]] std::optional + getDebugLocation(const llvm::Value *V); + + void print(llvm::raw_ostream &OS, ManagedDebugLocation Loc, + llvm::SourceMgr::DiagKind DiagKind, const llvm::Twine &Message); + +private: + llvm::DenseMap FileIdMap{}; + llvm::SourceMgr SrcMgr{}; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_UTILS_LLVMSOURCEMANAGER_H diff --git a/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h b/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h new file mode 100644 index 000000000..58b00e9a9 --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h @@ -0,0 +1,71 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_SOURCEMGRPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_SOURCEMGRPRINTER_H + +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMSourceManager.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/MaybeUniquePtr.h" + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +namespace detail { +class SourceMgrPrinterBase { +public: + explicit SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + llvm::raw_ostream &OS = llvm::errs(), + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning); + + explicit SourceMgrPrinterBase( + llvm::unique_function &&PrintMessage, + const llvm::Twine &OutFileName, + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning); + +protected: + LLVMSourceManager SrcMgr; + + llvm::unique_function GetPrintMessage; + MaybeUniquePtr OS = &llvm::errs(); + llvm::SourceMgr::DiagKind WarningKind; +}; +} // namespace detail + +template +class SourceMgrPrinter : public AnalysisPrinterBase, + private detail::SourceMgrPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit SourceMgrPrinter( + llvm::unique_function &&PrintMessage, + llvm::raw_ostream &OS = llvm::errs(), + llvm::SourceMgr::DiagKind WKind = llvm::SourceMgr::DK_Warning) + : detail::SourceMgrPrinterBase(std::move(PrintMessage), OS, WKind) {} + +private: + void doOnResult(n_t Inst, d_t Fact, l_t /*Value*/, + DataFlowAnalysisType AnalysisType) override { + auto SrcLoc = SrcMgr.getDebugLocation(Inst); + if constexpr (std::is_convertible_v) { + if (!SrcLoc) { + SrcLoc = + SrcMgr.getDebugLocation(static_cast(Fact)); + } + } + + if (SrcLoc) { + SrcMgr.print(*OS, *SrcLoc, WarningKind, GetPrintMessage(AnalysisType)); + } + } +}; + +} // namespace psr +#endif diff --git a/include/phasar/PhasarPass.h b/include/phasar/PhasarPass.h new file mode 100644 index 000000000..aa8eb9b98 --- /dev/null +++ b/include/phasar/PhasarPass.h @@ -0,0 +1,18 @@ +/****************************************************************************** + * Copyright (c) 2023 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_PHASARPASS_H +#define PHASAR_PHASARPASS_H + +#include "PhasarPass/Options.h" +#include "PhasarPass/PhasarPass.h" +#include "PhasarPass/PhasarPrinterPass.h" +#include "PhasarPass/RegisterPasses.h" + +#endif // PHASAR_PHASARPASS_H diff --git a/include/phasar/Pointer.h b/include/phasar/Pointer.h new file mode 100644 index 000000000..def25c239 --- /dev/null +++ b/include/phasar/Pointer.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2023 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_POINTER_H +#define PHASAR_POINTER_H + +#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" +#include "phasar/Pointer/PointsToInfoBase.h" + +#endif // PHASAR_POINTER_H diff --git a/include/phasar/Pointer/AliasAnalysisType.def b/include/phasar/Pointer/AliasAnalysisType.def index 23fbe08c7..93c3599d7 100644 --- a/include/phasar/Pointer/AliasAnalysisType.def +++ b/include/phasar/Pointer/AliasAnalysisType.def @@ -11,8 +11,9 @@ #define ALIAS_ANALYSIS_TYPE(NAME, CMDFLAG, DESC) #endif +ALIAS_ANALYSIS_TYPE(Basic, "basic", "Basic LLVM alias resolving based on simple, local properties") ALIAS_ANALYSIS_TYPE(CFLSteens, "cflsteens", "Steensgaard-style alias analysis (equality-based)") -ALIAS_ANALYSIS_TYPE(CFLAnders, "cflanders", "Andersen-style alias analysis (subset-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") #undef ALIAS_ANALYSIS_TYPE diff --git a/include/phasar/Pointer/AliasAnalysisType.h b/include/phasar/Pointer/AliasAnalysisType.h index c05177920..7e8fb61b1 100644 --- a/include/phasar/Pointer/AliasAnalysisType.h +++ b/include/phasar/Pointer/AliasAnalysisType.h @@ -1,5 +1,5 @@ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASANALYSISTYPE_H_ -#define PHASAR_PHASARLLVM_POINTER_ALIASANALYSISTYPE_H_ +#ifndef PHASAR_POINTER_ALIASANALYSISTYPE_H +#define PHASAR_POINTER_ALIASANALYSISTYPE_H #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -20,4 +20,4 @@ AliasAnalysisType toAliasAnalysisType(llvm::StringRef S); llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, AliasAnalysisType PA); } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_ALIASANALYSISTYPE_H_ +#endif // PHASAR_POINTER_ALIASANALYSISTYPE_H diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index eee1eca61..ba9fa480f 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASINFO_H_ -#define PHASAR_PHASARLLVM_POINTER_ALIASINFO_H_ +#ifndef PHASAR_POINTER_ALIASINFO_H +#define PHASAR_POINTER_ALIASINFO_H #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Pointer/AliasResult.h" diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index ae0488651..b3bb828c1 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASINFOBASE_H -#define PHASAR_PHASARLLVM_POINTER_ALIASINFOBASE_H +#ifndef PHASAR_POINTER_ALIASINFOBASE_H +#define PHASAR_POINTER_ALIASINFOBASE_H #include "phasar/Pointer/AliasInfoTraits.h" @@ -79,4 +79,4 @@ static constexpr bool IsAliasInfo = detail::IsAliasInfo::value; } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_ALIASINFOBASE_H +#endif // PHASAR_POINTER_ALIASINFOBASE_H diff --git a/include/phasar/Pointer/AliasInfoTraits.h b/include/phasar/Pointer/AliasInfoTraits.h index ad25d1141..4892db434 100644 --- a/include/phasar/Pointer/AliasInfoTraits.h +++ b/include/phasar/Pointer/AliasInfoTraits.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASINFOTRAITS_H -#define PHASAR_PHASARLLVM_POINTER_ALIASINFOTRAITS_H +#ifndef PHASAR_POINTER_ALIASINFOTRAITS_H +#define PHASAR_POINTER_ALIASINFOTRAITS_H #include "phasar/Utils/BoxedPointer.h" @@ -35,4 +35,4 @@ template struct DefaultAATraits { }; } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_ALIASINFOTRAITS_H +#endif // PHASAR_POINTER_ALIASINFOTRAITS_H diff --git a/include/phasar/Pointer/AliasResult.h b/include/phasar/Pointer/AliasResult.h index 29e62fff7..825d51ca0 100644 --- a/include/phasar/Pointer/AliasResult.h +++ b/include/phasar/Pointer/AliasResult.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASRESULT_H_ -#define PHASAR_PHASARLLVM_POINTER_ALIASRESULT_H_ +#ifndef PHASAR_POINTER_ALIASRESULT_H +#define PHASAR_POINTER_ALIASRESULT_H #include "llvm/Support/raw_ostream.h" @@ -27,4 +27,4 @@ AliasResult toAliasResult(llvm::StringRef S); llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, AliasResult AR); } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_ALIASRESULT_H_ +#endif // PHASAR_POINTER_ALIASRESULT_H diff --git a/include/phasar/Pointer/AliasSetOwner.h b/include/phasar/Pointer/AliasSetOwner.h index 5d01d79ba..405fca8c6 100644 --- a/include/phasar/Pointer/AliasSetOwner.h +++ b/include/phasar/Pointer/AliasSetOwner.h @@ -7,8 +7,8 @@ * Fabian Schiebel, Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_ALIASSETOWNER_H -#define PHASAR_PHASARLLVM_POINTER_ALIASSETOWNER_H +#ifndef PHASAR_POINTER_ALIASSETOWNER_H +#define PHASAR_POINTER_ALIASSETOWNER_H #include "phasar/Pointer/AliasInfoTraits.h" #include "phasar/Utils/BoxedPointer.h" @@ -66,7 +66,7 @@ template class AliasSetOwner { AliasSetOwner &operator=(AliasSetOwner &&) = delete; ~AliasSetOwner() { - for (auto PTS : OwnedPTS) { + for (auto *PTS : OwnedPTS) { std::destroy_at(PTS); #if HAS_MEMORY_RESOURCE Alloc.deallocate(PTS, 1); @@ -121,4 +121,4 @@ extern template class AliasSetOwner::AliasSetTy>; } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_ALIASSETOWNER_H +#endif // PHASAR_POINTER_ALIASSETOWNER_H diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index c5511ec2b..94bae1f3d 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_POINTSTOINFO_H -#define PHASAR_PHASARLLVM_POINTER_POINTSTOINFO_H +#ifndef PHASAR_POINTER_POINTSTOINFO_H +#define PHASAR_POINTER_POINTSTOINFO_H #include "phasar/Pointer/PointsToInfoBase.h" #include "phasar/Utils/ByRef.h" @@ -264,4 +264,4 @@ class [[clang::trivial_abi]] PointsToInfo< } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_POINTSTOINFO_H +#endif // PHASAR_POINTER_POINTSTOINFO_H diff --git a/include/phasar/Pointer/PointsToInfoBase.h b/include/phasar/Pointer/PointsToInfoBase.h index 923429d3c..696065646 100644 --- a/include/phasar/Pointer/PointsToInfoBase.h +++ b/include/phasar/Pointer/PointsToInfoBase.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_POINTER_POINTSTOINFOBASE_H -#define PHASAR_PHASARLLVM_POINTER_POINTSTOINFOBASE_H +#ifndef PHASAR_POINTER_POINTSTOINFOBASE_H +#define PHASAR_POINTER_POINTSTOINFOBASE_H #include "phasar/Utils/ByRef.h" #include "phasar/Utils/TypeTraits.h" @@ -145,4 +145,4 @@ template class PointsToInfoBase { } // namespace psr -#endif // PHASAR_PHASARLLVM_POINTER_POINTSTOINFOBASE_H +#endif // PHASAR_POINTER_POINTSTOINFOBASE_H diff --git a/include/phasar/TypeHierarchy.h b/include/phasar/TypeHierarchy.h new file mode 100644 index 000000000..d49fab367 --- /dev/null +++ b/include/phasar/TypeHierarchy.h @@ -0,0 +1,16 @@ +/****************************************************************************** + * Copyright (c) 2023 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_TYPEHIERARCHY_H +#define PHASAR_TYPEHIERARCHY_H + +#include "phasar/TypeHierarchy/TypeHierarchy.h" +#include "phasar/TypeHierarchy/VFTable.h" + +#endif // PHASAR_TYPEHIERARCHY_H diff --git a/include/phasar/TypeHierarchy/TypeHierarchy.h b/include/phasar/TypeHierarchy/TypeHierarchy.h index db9d9c8db..e3f44e879 100644 --- a/include/phasar/TypeHierarchy/TypeHierarchy.h +++ b/include/phasar/TypeHierarchy/TypeHierarchy.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_TYPEHIERARCHY_H_ -#define PHASAR_PHASARLLVM_TYPEHIERARCHY_TYPEHIERARCHY_H_ +#ifndef PHASAR_TYPEHIERARCHY_TYPEHIERARCHY_H +#define PHASAR_TYPEHIERARCHY_TYPEHIERARCHY_H #include "phasar/TypeHierarchy/VFTable.h" diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index bfbd615e0..56334171b 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -7,8 +7,8 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_VFTABLE_H_ -#define PHASAR_PHASARLLVM_TYPEHIERARCHY_VFTABLE_H_ +#ifndef PHASAR_TYPEHIERARCHY_VFTABLE_H +#define PHASAR_TYPEHIERARCHY_VFTABLE_H #include "llvm/Support/raw_ostream.h" diff --git a/include/phasar/Utils.h b/include/phasar/Utils.h new file mode 100644 index 000000000..f120b4faa --- /dev/null +++ b/include/phasar/Utils.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (c) 2023 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_H +#define PHASAR_UTILS_H + +#include "phasar/Utils/AnalysisProperties.h" +#include "phasar/Utils/BitVectorSet.h" +#include "phasar/Utils/BoxedPointer.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/DOTGraph.h" +#include "phasar/Utils/DebugOutput.h" +#include "phasar/Utils/EnumFlags.h" +#include "phasar/Utils/EquivalenceClassMap.h" +#include "phasar/Utils/ErrorHandling.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/MemoryResource.h" +#include "phasar/Utils/NlohmannLogging.h" +#include "phasar/Utils/Nullable.h" +#include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/Soundness.h" +#include "phasar/Utils/StableVector.h" +#include "phasar/Utils/Table.h" +#include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" + +#endif // PHASAR_UTILS_H diff --git a/include/phasar/Utils/AdjacencyList.h b/include/phasar/Utils/AdjacencyList.h new file mode 100644 index 000000000..e09bb99fa --- /dev/null +++ b/include/phasar/Utils/AdjacencyList.h @@ -0,0 +1,301 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_ADJACENCYLIST_H +#define PHASAR_UTILS_ADJACENCYLIST_H + +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/IotaIterator.h" +#include "phasar/Utils/RepeatIterator.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include +#include + +namespace psr { + +template struct AdjacencyList { + llvm::SmallVector Nodes{}; + llvm::SmallVector, 0> Adj{}; + llvm::SmallVector Roots{}; +}; + +template struct AdjacencyList { + llvm::SmallVector, 0> Adj{}; + llvm::SmallVector Roots{}; +}; + +/// A simple graph implementation based on an adjacency list +template +struct GraphTraits> { + using graph_type = AdjacencyList; + using value_type = T; + using vertex_t = unsigned; + using edge_t = EdgeTy; + using edge_iterator = typename llvm::ArrayRef::const_iterator; + using roots_iterator = typename llvm::ArrayRef::const_iterator; + + /// A vertex that is not inserted into any graph. Can be used to communicate + /// failure of certain operations + static inline constexpr auto Invalid = std::numeric_limits::max(); + + /// Adds a new node to the graph G with node-tag Val + /// + /// \returns The vertex-descriptor for the newly created node + template >> + static vertex_t addNode(graph_type &G, TT &&Val) { + assert(G.Adj.size() == G.Nodes.size()); + + auto Ret = G.Nodes.size(); + G.Nodes.push_back(std::forward(Val)); + G.Adj.emplace_back(); + return Ret; + } + + /// Adds a new node to the graph G without node-tag + /// + /// \returns The vertex-descriptor for the newly created node + template >> + static vertex_t addNode(graph_type &G, llvm::NoneType /*Val*/ = llvm::None) { + auto Ret = G.Adj.size(); + G.Adj.emplace_back(); + return Ret; + } + + /// Makes the node Vtx as root in the graph G. A node should not be registered + /// as root multiple times + static void addRoot(graph_type &G, vertex_t Vtx) { + assert(Vtx < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + G.Roots.push_back(Vtx); + } + + /// Gets a range of all root nodes of graph G + static llvm::ArrayRef roots(const graph_type &G) noexcept { + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return G.Roots; + } + + /// Adds a new edge from node From to node To in graph G. From and To should + /// be nodes inside G. Multi-edges are supported, i.e. edges are not + /// deduplicated automatically; to manually deduplicate the edges of one + /// source-node, call dedupOutEdges() + static void addEdge(graph_type &G, vertex_t From, edge_t To) { + assert(From < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + G.Adj[From].push_back(std::move(To)); + } + + /// Gets a range of all edges outgoing from node Vtx in graph G + static llvm::ArrayRef outEdges(const graph_type &G, + vertex_t Vtx) noexcept { + assert(Vtx < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return G.Adj[Vtx]; + } + + /// Gets the number of edges outgoing from node Vtx in graph G + static size_t outDegree(const graph_type &G, vertex_t Vtx) noexcept { + assert(Vtx < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return G.Adj[Vtx].size(); + } + + /// Deduplicates the edges outgoing from node Vtx in graph G. Deduplication is + /// based on operator< and operator== of the edge_t type + static void dedupOutEdges(graph_type &G, vertex_t Vtx) noexcept { + assert(Vtx < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + auto &OutEdges = G.Adj[Vtx]; + std::sort(OutEdges.begin(), OutEdges.end()); + OutEdges.erase(std::unique(OutEdges.begin(), OutEdges.end()), + OutEdges.end()); + } + + /// Gets a const range of all nodes in graph G + template >> + static llvm::ArrayRef nodes(const graph_type &G) noexcept { + assert(G.Adj.size() == G.Nodes.size()); + return G.Nodes; + } + /// Gets a mutable range of all nodes in graph G + template >> + static llvm::MutableArrayRef nodes(graph_type &G) noexcept { + assert(G.Adj.size() == G.Nodes.size()); + return G.Nodes; + } + /// Gets a range of all nodes in graph G + template >> + static RepeatRangeType nodes(const graph_type &G) noexcept { + return repeat(llvm::None, G.Adj.size()); + } + + /// Gets a range of vertex-descriptors for all nodes in graph G + static auto vertices(const graph_type &G) noexcept { + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return psr::iota(size_t(0), G.Adj.size()); + } + + /// Gets the node-tag for node Vtx in graph G. Vtx must be part of G + template >> + static const value_type &node(const graph_type &G, vertex_t Vtx) noexcept { + assert(Vtx < G.Nodes.size()); + assert(G.Adj.size() == G.Nodes.size()); + return G.Nodes[Vtx]; + } + /// Gets the node-tag for node Vtx in graph G. Vtx must be part of G + template >> + static value_type &node(graph_type &G, vertex_t Vtx) noexcept { + assert(Vtx < G.Nodes.size()); + assert(G.Adj.size() == G.Nodes.size()); + return G.Nodes[Vtx]; + } + + /// Gets the node-tag for node Vtx in graph G. Vtx must be part of G + template >> + static llvm::NoneType node([[maybe_unused]] const graph_type &G, + [[maybe_unused]] vertex_t Vtx) noexcept { + assert(Vtx < G.Adj.size()); + return llvm::None; + } + + /// Gets the number of nodes in graph G + static size_t size(const graph_type &G) noexcept { + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return G.Adj.size(); + } + + /// Gets the number of nodes in graph G that are marked as root + static size_t roots_size(const graph_type &G) noexcept { // NOLINT + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + return G.Roots.size(); + } + + /// Pre-allocates space to hold up to Capacity nodes + static void reserve(graph_type &G, size_t Capacity) { + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + G.Nodes.reserve(Capacity); + } + + G.Adj.reserve(Capacity); + } + + /// Tries to remove the last inserted node Vtx fro graph G. Fails, if there + /// was another not-popped node inserted in between. + /// + /// \returns True, iff the removal was successful + static bool pop(graph_type &G, vertex_t Vtx) { + if (Vtx == G.Adj.size() - 1) { + G.Adj.pop_back(); + if constexpr (!std::is_same_v) { + G.Nodes.pop_back(); + } + return true; + } + return false; + } + + /// Gets the vertex-descriptor of the target-node of the given Edge + template + static std::enable_if_t, vertex_t> + target(edge_t Edge) noexcept { + return Edge; + } + + /// Copies the given edge, but replaces the target node by Tar; i.e. the + /// weight of the returned edge and the parameter edge is same, but the target + /// nodes may differ. + template + static std::enable_if_t, edge_t> + withEdgeTarget(edge_t /*edge*/, vertex_t Tar) noexcept { + return Tar; + } + + /// Gets the weight associated with the given edge + static llvm::NoneType weight(edge_t /*unused*/) noexcept { + return llvm::None; + } + + /// Removes the edge denoted by It outgoing from source-vertex Vtx from the + /// graph G. This function is not required by the is_graph_trait concept. + /// + /// \returns An edge_iterator directly following It that should be used to + /// continue iteration instead of std::next(It) + static edge_iterator removeEdge(graph_type &G, vertex_t Vtx, + edge_iterator It) noexcept { + assert(Vtx < G.Adj.size()); + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + assert(G.Adj[Vtx].begin() <= It && It < G.Adj[Vtx].end()); + auto Idx = std::distance(std::cbegin(G.Adj[Vtx]), It); + + std::swap(G.Adj[Vtx][Idx], G.Adj[Vtx].back()); + G.Adj[Vtx].pop_back(); + return It; + } + + /// Removes the root denoted by It from the graph G. This function is not + /// required by the is_graph_trait concept. + /// + /// \returns A roots_iterator directly following It that should be used to + /// continue iteration instead of std::next(It) + static roots_iterator removeRoot(graph_type &G, roots_iterator It) noexcept { + if constexpr (!std::is_same_v) { + assert(G.Adj.size() == G.Nodes.size()); + } + assert(G.Roots.begin() <= It && It < G.Roots.end()); + + auto Idx = std::distance(std::cbegin(G.Roots), It); + std::swap(G.Roots[Idx], G.Roots.back()); + G.Roots.pop_back(); + return It; + } + +#if __cplusplus >= 202002L + static_assert(is_graph>); + static_assert(is_reservable_graph_trait>>); +#endif +}; + +} // namespace psr + +#endif // PHASAR_UTILS_ADJACENCYLIST_H diff --git a/include/phasar/Utils/AnalysisPrinterBase.h b/include/phasar/Utils/AnalysisPrinterBase.h new file mode 100644 index 000000000..a1ca6aee3 --- /dev/null +++ b/include/phasar/Utils/AnalysisPrinterBase.h @@ -0,0 +1,54 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H +#define PHASAR_PHASARLLVM_UTILS_ANALYSISPRINTERBASE_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +template class AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + template , psr::BinaryDomain>>> + void onResult(n_t Instr, d_t DfFact, l_t LatticeElement, + DataFlowAnalysisType AnalysisType) { + doOnResult(Instr, DfFact, LatticeElement, AnalysisType); + } + + template , psr::BinaryDomain>>> + void onResult(n_t Instr, d_t DfFact, DataFlowAnalysisType AnalysisType) { + doOnResult(Instr, DfFact, psr::BinaryDomain::BOTTOM, AnalysisType); + } + + void onInitialize() { doOnInitialize(); } + + void onFinalize() { doOnFinalize(); } + + AnalysisPrinterBase() = default; + virtual ~AnalysisPrinterBase() = default; + AnalysisPrinterBase(const AnalysisPrinterBase &) = delete; + AnalysisPrinterBase &operator=(const AnalysisPrinterBase &) = delete; + + AnalysisPrinterBase(AnalysisPrinterBase &&) = delete; + AnalysisPrinterBase &operator=(AnalysisPrinterBase &&) = delete; + +private: + virtual void doOnResult(n_t /*Instr*/, d_t /*DfFact*/, l_t /*LatticeElement*/, + DataFlowAnalysisType /*AnalysisType*/) = 0; + + virtual void doOnInitialize() {} + virtual void doOnFinalize() {} +}; + +} // namespace psr + +#endif diff --git a/include/phasar/Utils/Average.h b/include/phasar/Utils/Average.h new file mode 100644 index 000000000..c6e54f090 --- /dev/null +++ b/include/phasar/Utils/Average.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_AVERAGE_H +#define PHASAR_UTILS_AVERAGE_H + +#include + +namespace psr { + +struct Sampler { + size_t Count{}; + double Curr{}; + + constexpr void addSample(size_t Sample) noexcept { + Curr += (double(Sample) - Curr) / double(++Count); + } + + [[nodiscard]] constexpr double getAverage() const noexcept { return Curr; } + [[nodiscard]] constexpr size_t getNumSamples() const noexcept { + return Count; + } +}; + +} // namespace psr + +#endif // PHASAR_UTILS_AVERAGE_H diff --git a/include/phasar/Utils/ByRef.h b/include/phasar/Utils/ByRef.h index 4cd72f550..4e0f21bed 100644 --- a/include/phasar/Utils/ByRef.h +++ b/include/phasar/Utils/ByRef.h @@ -7,8 +7,8 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_UTILS_BYREF_H -#define PHASAR_PHASARLLVM_UTILS_BYREF_H +#ifndef PHASAR_UTILS_BYREF_H +#define PHASAR_UTILS_BYREF_H #include @@ -26,4 +26,4 @@ using ByMoveRef = std::conditional_t, T, T &&>; } // namespace psr -#endif // PHASAR_PHASARLLVM_UTILS_BYREF_H +#endif // PHASAR_UTILS_BYREF_H diff --git a/include/phasar/Utils/ChronoUtils.h b/include/phasar/Utils/ChronoUtils.h new file mode 100644 index 000000000..ca4f4092f --- /dev/null +++ b/include/phasar/Utils/ChronoUtils.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * Copyright (c) 2023 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_CHRONOUTILS_H +#define PHASAR_UTILS_CHRONOUTILS_H + +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +/// Simple struct that allows formatting of time-durations as +/// hours:minutes:seconds.microseconds. +/// +/// \remark This feature may come into C++23, so until then, use this one. +struct hms { // NOLINT + std::chrono::hours Hours{}; + std::chrono::minutes Minutes{}; + std::chrono::seconds Seconds{}; + std::chrono::microseconds Micros{}; + + hms() noexcept = default; + hms(std::chrono::nanoseconds NS) noexcept { + using namespace std::chrono; + + Hours = duration_cast(NS); + NS -= Hours; + Minutes = duration_cast(NS); + NS -= Minutes; + Seconds = duration_cast(NS); + NS -= Seconds; + Micros = duration_cast(NS); + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const hms &HMS); + + [[nodiscard]] std::string str() const { + std::string Ret; + llvm::raw_string_ostream OS(Ret); + OS << *this; + return Ret; + } +}; + +} // namespace psr + +#endif // PHASAR_UTILS_CHRONOUTILS_H diff --git a/include/phasar/Utils/DFAMinimizer.h b/include/phasar/Utils/DFAMinimizer.h new file mode 100644 index 000000000..9c08a3b7c --- /dev/null +++ b/include/phasar/Utils/DFAMinimizer.h @@ -0,0 +1,194 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_DFAMINIMIZER_H +#define PHASAR_UTILS_DFAMINIMIZER_H + +#include "phasar/Utils/GraphTraits.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/IntEqClasses.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallVector.h" + +namespace psr { + +template +[[nodiscard]] std::decay_t +createEquivalentGraphFrom(GraphTy &&G, const llvm::IntEqClasses &Eq) +#if __cplusplus >= 202002L + requires is_graph +#endif +{ + using traits_t = GraphTraits; + using vertex_t = typename traits_t::vertex_t; + + std::decay_t Ret; + + llvm::SmallBitVector Handled(traits_t::size(G)); + llvm::SmallVector Eq2Vtx(Eq.getNumClasses(), traits_t::Invalid); + llvm::SmallVector WorkList; + + if constexpr (is_reservable_graph_trait_v) { + traits_t::reserve(Ret, Eq.getNumClasses()); + } + + for (auto Rt : traits_t::roots(G)) { + auto EqRt = Eq[Rt]; + + if (Eq2Vtx[EqRt] == traits_t::Invalid) { + auto NwRt = + traits_t::addNode(Ret, forward_like(traits_t::node(G, Rt))); + Eq2Vtx[EqRt] = NwRt; + traits_t::addRoot(Ret, NwRt); + } + WorkList.push_back(Rt); + } + + while (!WorkList.empty()) { + auto Vtx = WorkList.pop_back_val(); + auto EqVtx = Eq[Vtx]; + + auto NwVtx = Eq2Vtx[EqVtx]; + assert(NwVtx != traits_t::Invalid); + + Handled.set(Vtx); + + for (const auto &Edge : traits_t::outEdges(G, Vtx)) { + auto Target = traits_t::target(Edge); + auto EqTarget = Eq[Target]; + if (Eq2Vtx[EqTarget] == traits_t::Invalid) { + Eq2Vtx[EqTarget] = traits_t::addNode( + Ret, forward_like(traits_t::node(G, Target))); + } + if (!Handled.test(Target)) { + WorkList.push_back(Target); + } + auto NwTarget = Eq2Vtx[EqTarget]; + traits_t::addEdge(Ret, NwVtx, traits_t::withEdgeTarget(Edge, NwTarget)); + } + } + + for (auto Vtx : traits_t::vertices(Ret)) { + traits_t::dedupOutEdges(Ret, Vtx); + } + + return Ret; +} + +template +[[nodiscard]] llvm::IntEqClasses minimizeGraph(const GraphTy &G) +#if __cplusplus >= 202002L + requires is_graph +#endif +{ + + using traits_t = GraphTraits; + using vertex_t = typename traits_t::vertex_t; + using edge_t = typename traits_t::edge_t; + + auto DagSize = traits_t::size(G); + llvm::SmallVector> WorkList; + WorkList.reserve(DagSize); + + const auto &Vertices = traits_t::vertices(G); + + for (auto VtxBegin = Vertices.begin(), VtxEnd = Vertices.end(); + VtxBegin != VtxEnd; ++VtxBegin) { + for (auto Inner = std::next(VtxBegin); Inner != VtxEnd; ++Inner) { + if (traits_t::node(G, *VtxBegin) == traits_t::node(G, *Inner) && + traits_t::outDegree(G, *VtxBegin) == traits_t::outDegree(G, *Inner)) { + WorkList.emplace_back(*VtxBegin, *Inner); + } + } + } + + llvm::IntEqClasses Equiv(traits_t::size(G)); + + auto isEquivalent = [&Equiv](edge_t LHS, edge_t RHS) { + if (traits_t::weight(LHS) != traits_t::weight(RHS)) { + return false; + } + + if (traits_t::target(LHS) == traits_t::target(RHS)) { + return true; + } + + return Equiv.findLeader(traits_t::target(LHS)) == + Equiv.findLeader(traits_t::target(RHS)); + }; + + auto makeEquivalent = [&Equiv](vertex_t LHS, vertex_t RHS) { + if (LHS == RHS) { + return; + } + + Equiv.join(LHS, RHS); + }; + + auto removeAt = [&WorkList](size_t I) { + std::swap(WorkList[I], WorkList.back()); + WorkList.pop_back(); + return I - 1; + }; + + /// NOTE: This algorithm can be further optimized, but for now it is fast + /// enough + + bool Changed = true; + while (Changed) { + Changed = false; + for (size_t I = 0; I < WorkList.size(); ++I) { + auto [LHS, RHS] = WorkList[I]; + bool Eq = true; + for (auto [LSucc, RSucc] : + llvm::zip(traits_t::outEdges(G, LHS), traits_t::outEdges(G, RHS))) { + if (!isEquivalent(LSucc, RSucc)) { + Eq = false; + break; + } + } + + if (Eq) { + makeEquivalent(LHS, RHS); + I = removeAt(I); + Changed = true; + continue; + } + + if (traits_t::outDegree(G, LHS) == 2) { + auto LFirst = *traits_t::outEdges(G, LHS).begin(); + auto LSecond = *std::next(traits_t::outEdges(G, LHS).begin()); + auto RFirst = *traits_t::outEdges(G, RHS).begin(); + auto RSecond = *std::next(traits_t::outEdges(G, RHS).begin()); + + if (isEquivalent(LFirst, RSecond) && isEquivalent(LSecond, RFirst)) { + makeEquivalent(LHS, RHS); + I = removeAt(I); + Changed = true; + continue; + } + } + } + } + + Equiv.compress(); + + PHASAR_LOG_LEVEL_CAT(DEBUG, "GraphTraits", + "> Computed " << Equiv.getNumClasses() + << " Equivalence classes for " << DagSize + << " nodes"); + + return Equiv; +} + +} // namespace psr + +#endif // PHASAR_UTILS_DFAMINIMIZER_H diff --git a/include/phasar/Utils/DOTGraph.h b/include/phasar/Utils/DOTGraph.h index cae66e6fc..b16777c9f 100644 --- a/include/phasar/Utils/DOTGraph.h +++ b/include/phasar/Utils/DOTGraph.h @@ -14,8 +14,8 @@ * Author: rleer */ -#ifndef PHASAR_PHASARLLVM_UTILS_DOTGRAPH_H_ -#define PHASAR_PHASARLLVM_UTILS_DOTGRAPH_H_ +#ifndef PHASAR_UTILS_DOTGRAPH_H +#define PHASAR_UTILS_DOTGRAPH_H #include "phasar/Config/Configuration.h" #include "phasar/Utils/Utilities.h" diff --git a/include/phasar/Utils/DefaultAnalysisPrinter.h b/include/phasar/Utils/DefaultAnalysisPrinter.h new file mode 100644 index 000000000..f9008b946 --- /dev/null +++ b/include/phasar/Utils/DefaultAnalysisPrinter.h @@ -0,0 +1,73 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_DEFAULTANALYSISPRINTER_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/Printer.h" + +#include +#include +#include + +namespace psr { + +template struct Warning { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + + n_t Instr; + d_t Fact; + l_t LatticeElement; + DataFlowAnalysisType AnalysisType; + + // Constructor + Warning(n_t Inst, d_t DfFact, l_t Lattice, + DataFlowAnalysisType DfAnalysisType) + : Instr(std::move(Inst)), Fact(std::move(DfFact)), + LatticeElement(std::move(Lattice)), AnalysisType(DfAnalysisType) {} +}; + +template +class DefaultAnalysisPrinter : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit DefaultAnalysisPrinter(llvm::raw_ostream &OS = llvm::outs()) + : OS(&OS) {} + + explicit DefaultAnalysisPrinter(const llvm::Twine &Filename) + : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + + ~DefaultAnalysisPrinter() override = default; + +private: + void doOnResult(n_t Instr, d_t DfFact, l_t Lattice, + DataFlowAnalysisType AnalysisType) override { + AnalysisResults.emplace_back(Instr, DfFact, Lattice, AnalysisType); + } + + void doOnFinalize() override { + for (const auto &Iter : AnalysisResults) { + + *OS << "\nAt IR statement: " << NToString(Iter.Instr) << "\n"; + + *OS << "\tFact: " << DToString(Iter.Fact) << "\n"; + + if constexpr (!std::is_same_v) { + *OS << "Value: " << LToString(Iter.LatticeElement) << "\n"; + } + } + } + + std::vector> AnalysisResults{}; + MaybeUniquePtr OS = &llvm::outs(); +}; + +} // namespace psr + +#endif diff --git a/include/phasar/Utils/DefaultValue.h b/include/phasar/Utils/DefaultValue.h new file mode 100644 index 000000000..3dde71aeb --- /dev/null +++ b/include/phasar/Utils/DefaultValue.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (c) 2023 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_DEFAULTVALUE_H +#define PHASAR_UTILS_DEFAULTVALUE_H + +#include "phasar/Utils/ByRef.h" + +#include + +namespace psr { + +/// Gets a (cached) reference to the default-constructed value of type T. If T +/// is small and trivially default constructible, creates a temporary instead. +/// Useful for getters that return ByConstRef but need to handle the +/// non-existing-T case +template >> +[[nodiscard]] ByConstRef +getDefaultValue() noexcept(std::is_nothrow_default_constructible_v) { + auto DefaultConstruct = [] { + if constexpr (std::is_aggregate_v) { + return T{}; + } else { + return T(); + } + }; + + if constexpr (CanEfficientlyPassByValue) { + return DefaultConstruct(); + } else { + static T DefaultVal = DefaultConstruct(); + return DefaultVal; + } +} + +} // namespace psr + +#endif // PHASAR_UTILS_DEFAULTVALUE_H diff --git a/include/phasar/Utils/EquivalenceClassMap.h b/include/phasar/Utils/EquivalenceClassMap.h index 7c84a168d..c93508fe3 100644 --- a/include/phasar/Utils/EquivalenceClassMap.h +++ b/include/phasar/Utils/EquivalenceClassMap.h @@ -10,6 +10,7 @@ #ifndef PHASAR_UTILS_EQUIVALENCECLASSMAP_H #define PHASAR_UTILS_EQUIVALENCECLASSMAP_H +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/iterator_range.h" #include @@ -148,10 +149,10 @@ template struct EquivalenceClassMap { } [[nodiscard]] const_iterator find(key_type Key) const { - return find_if(StoredData.begin(), StoredData.end(), - [&Key](const EquivalenceClassBucketT &Val) -> bool { - return Val.first.count(Key) >= 1; - }); + return llvm::find_if(StoredData, + [&Key](const EquivalenceClassBucketT &Val) -> bool { + return Val.first.count(Key) >= 1; + }); } [[nodiscard]] std::optional findValue(key_type Key) const { diff --git a/include/phasar/Utils/GraphTraits.h b/include/phasar/Utils/GraphTraits.h new file mode 100644 index 000000000..be00b4b84 --- /dev/null +++ b/include/phasar/Utils/GraphTraits.h @@ -0,0 +1,213 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_GRAPHTRAITS_H +#define PHASAR_UTILS_GRAPHTRAITS_H + +#include "phasar/Utils/TypeTraits.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/identity.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include + +namespace psr { + +/// We aim to get rid of boost, so introduce a new GraphTraits class to replace +/// it. +/// This GraphTraits type should be specialized for each type that implements a +/// "graph". All the functionality should be reflected by the GraphTraits class. +/// Once moving to C++20, we have nice type-checking using concepts +template struct GraphTraits; + +#if __cplusplus >= 202002L + +template +concept is_graph_edge = requires(const Edge e1, Edge e2) { + { e1 == e2 } -> std::convertible_to; + { e1 != e2 } -> std::convertible_to; + { e1 < e2 } -> std::convertible_to; +}; + +template +concept is_graph_trait = requires(typename GraphTrait::graph_type &graph, + const typename GraphTrait::graph_type &cgraph, + typename GraphTrait::value_type val, + typename GraphTrait::vertex_t vtx, + typename GraphTrait::edge_t edge) { + typename GraphTrait::graph_type; + typename GraphTrait::value_type; + typename GraphTrait::vertex_t; + typename GraphTrait::edge_t; + requires is_graph_edge; + { GraphTrait::Invalid } -> std::convertible_to; + { + GraphTrait::addNode(graph, val) + } -> std::convertible_to; + {GraphTrait::addEdge(graph, vtx, edge)}; + { + GraphTrait::outEdges(cgraph, vtx) + } -> psr::is_iterable_over_v; + { GraphTrait::outDegree(cgraph, vtx) } -> std::convertible_to; + {GraphTrait::dedupOutEdges(graph, vtx)}; + { + GraphTrait::nodes(cgraph) + } -> psr::is_iterable_over_v; + { + GraphTrait::vertices(cgraph) + } -> psr::is_iterable_over_v; + { + GraphTrait::node(cgraph, vtx) + } -> std::convertible_to; + { GraphTrait::size(cgraph) } -> std::convertible_to; + {GraphTrait::addRoot(graph, vtx)}; + { + GraphTrait::roots(cgraph) + } -> psr::is_iterable_over_v; + { GraphTrait::pop(graph, vtx) } -> std::same_as; + { GraphTrait::roots_size(cgraph) } -> std::convertible_to; + { + GraphTrait::target(edge) + } -> std::convertible_to; + { + GraphTrait::withEdgeTarget(edge, vtx) + } -> std::convertible_to; + {GraphTrait::weight(edge)}; +}; + +template +concept is_graph = requires(Graph g) { + typename GraphTraits>; + requires is_graph_trait>>; +}; + +template +concept is_reservable_graph_trait_v = is_graph_trait && + requires(typename GraphTrait::graph_type &g) { + {GraphTrait::reserve(g, size_t(0))}; +}; + +#else +namespace detail { +template +// NOLINTNEXTLINE(readability-identifier-naming) +struct is_reservable_graph_trait : std::false_type {}; +template +struct is_reservable_graph_trait< + GraphTrait, + std::void_t(), size_t()))>> + : std::true_type {}; + +template +// NOLINTNEXTLINE(readability-identifier-naming) +struct is_removable_graph_trait : std::false_type {}; +template +struct is_removable_graph_trait< + GraphTrait, + std::void_t(), + std::declval(), + std::declval())), + decltype(GraphTrait::removeRoot( + std::declval(), + std::declval()))>> + : std::true_type {}; +} // namespace detail + +template +// NOLINTNEXTLINE(readability-identifier-naming) +static constexpr bool is_reservable_graph_trait_v = + detail::is_reservable_graph_trait::value; + +template +// NOLINTNEXTLINE(readability-identifier-naming) +static constexpr bool is_removable_graph_trait_v = + detail::is_removable_graph_trait::value; +#endif + +template +std::decay_t reverseGraph(GraphTy &&G) +#if __cplusplus >= 202002L + requires is_graph +#endif +{ + std::decay_t Ret; + using traits_t = GraphTraits>; + if constexpr (is_reservable_graph_trait_v) { + traits_t::reserve(Ret, traits_t::size(G)); + } + + for (auto &Nod : traits_t::nodes(G)) { + traits_t::addNode(Ret, forward_like(Nod)); + } + for (auto I : traits_t::vertices(G)) { + for (auto Child : traits_t::outEdges(G, I)) { + traits_t::addEdge(Ret, traits_t::target(Child), + traits_t::withEdgeTarget(Child, I)); + } + if (traits_t::outDegree(G, I) == 0) { + traits_t::addRoot(Ret, I); + } + } + return Ret; +} + +struct DefaultNodeTransform { + template std::string operator()(const N &Nod) const { + std::string Buf; + llvm::raw_string_ostream ROS(Buf); + ROS << Nod; + ROS.flush(); + return Buf; + } +}; + +template +void printGraph(const GraphTy &G, llvm::raw_ostream &OS, + llvm::StringRef Name = "", NodeTransform NodeToString = {}) +#if __cplusplus >= 202002L + requires is_graph +#endif +{ + using traits_t = GraphTraits; + + OS << "digraph " << Name << " {\n"; + psr::scope_exit CloseBrace = [&OS] { OS << "}\n"; }; + + auto Sz = traits_t::size(G); + + for (size_t I = 0; I < Sz; ++I) { + OS << I; + if constexpr (!std::is_same_v) { + OS << "[label=\""; + OS.write_escaped(std::invoke(NodeToString, traits_t::node(G, I))); + OS << "\"]"; + } + OS << ";\n"; + for (const auto &Edge : traits_t::outEdges(G, I)) { + OS << I << "->" << Edge << ";\n"; + } + } +} + +} // namespace psr + +#endif // PHASAR_UTILS_GRAPHTRAITS_H diff --git a/include/phasar/Utils/IotaIterator.h b/include/phasar/Utils/IotaIterator.h new file mode 100644 index 000000000..9eab786b6 --- /dev/null +++ b/include/phasar/Utils/IotaIterator.h @@ -0,0 +1,73 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_IOTAITERATOR_H +#define PHASAR_UTILS_IOTAITERATOR_H + +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/iterator_range.h" + +#include +#include +#include +#include + +namespace psr { +/// An iterator that iterates over the same value a specified number of times +template class IotaIterator { +public: + using value_type = T; + using reference = T; + using pointer = const T *; + using difference_type = ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr reference operator*() const noexcept { return Elem; } + constexpr pointer operator->() const noexcept { return &Elem; } + + constexpr IotaIterator &operator++() noexcept { + ++Elem; + return *this; + } + constexpr IotaIterator operator++(int) noexcept { + auto Ret = *this; + ++*this; + return Ret; + } + + constexpr bool operator==(const IotaIterator &Other) const noexcept { + return Other.Elem == Elem; + } + constexpr bool operator!=(const IotaIterator &Other) const noexcept { + return !(*this == Other); + } + + constexpr explicit IotaIterator(T Elem) noexcept : Elem(Elem) {} + + constexpr IotaIterator() noexcept = default; + +private: + T Elem{}; +}; + +template +using IotaRangeType = llvm::iterator_range>; +template constexpr auto iota(T From, T To) noexcept { + static_assert(std::is_integral_v, "Iota only works on integers"); + using iterator_type = IotaIterator>; + auto Ret = llvm::make_range(iterator_type(From), iterator_type(To)); + return Ret; +} + +static_assert(is_iterable_over_v, int>); + +} // namespace psr + +#endif // PHASAR_UTILS_IOTAITERATOR_H diff --git a/include/phasar/Utils/JoinLattice.h b/include/phasar/Utils/JoinLattice.h index 718eaf6f3..8e748cf78 100644 --- a/include/phasar/Utils/JoinLattice.h +++ b/include/phasar/Utils/JoinLattice.h @@ -14,8 +14,8 @@ * Author: pdschbrt */ -#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_JOINLATTICE_H -#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_JOINLATTICE_H +#ifndef PHASAR_UTILS_JOINLATTICE_H +#define PHASAR_UTILS_JOINLATTICE_H #include #include diff --git a/include/phasar/Utils/Logger.h b/include/phasar/Utils/Logger.h index 9dc9306b4..6b3ea5e18 100644 --- a/include/phasar/Utils/Logger.h +++ b/include/phasar/Utils/Logger.h @@ -10,17 +10,14 @@ #ifndef PHASAR_UTILS_LOGGER_H #define PHASAR_UTILS_LOGGER_H -#include "llvm/ADT/StringMap.h" +#include "phasar/Config/phasar-config.h" + #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" // LLVM_UNLIKELY -#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" -#include #include #include -#include -#include namespace psr { @@ -30,33 +27,40 @@ enum SeverityLevel { INVALID }; -SeverityLevel parseSeverityLevel(llvm::StringRef Str); +[[nodiscard]] SeverityLevel parseSeverityLevel(llvm::StringRef Str) noexcept; +[[nodiscard]] llvm::StringRef to_string(SeverityLevel Level) noexcept; class Logger final { public: /** * Set the filter level. */ - static void setLoggerFilterLevel(SeverityLevel Level); - - static SeverityLevel getLoggerFilterLevel(); + static void setLoggerFilterLevel(SeverityLevel Level) noexcept; - static bool isLoggingEnabled(); + static SeverityLevel getLoggerFilterLevel() noexcept { + return LogFilterLevel; + } - static void enable() { LoggingEnabled = true; }; + static bool isLoggingEnabled() noexcept { return LoggingEnabled; } - static void disable() { LoggingEnabled = false; }; + static void enable() noexcept { LoggingEnabled = true; }; + static void disable() noexcept { LoggingEnabled = false; }; static llvm::raw_ostream & getLogStream(std::optional Level, const std::optional &Category); - static bool logCategory(llvm::StringRef Category, - std::optional Level); + static llvm::raw_ostream & + getLogStreamWithLinePrefix(std::optional Level, + const std::optional &Category); + + [[nodiscard]] static bool + logCategory(llvm::StringRef Category, + std::optional Level) noexcept; static void addLinePrefix(llvm::raw_ostream &, std::optional Level, - const std::optional &Category); + const std::optional &Category); static void initializeStdoutLogger( std::optional Level = std::nullopt, @@ -73,87 +77,82 @@ class Logger final { bool Append = false); private: - enum class StdStream : uint8_t { STDOUT = 0, STDERR }; static inline bool LoggingEnabled = false; - static inline llvm::StringMap, - std::variant>> - CategoriesToStreamVariant; - static inline std::map, - std::variant> - LevelsToStreamVariant; - static inline SeverityLevel LogFilterLevel = DEBUG; - static std::string toString(SeverityLevel Level); - static inline llvm::StringMap LogfileStreams; - // static inline auto StartTime = std::chrono::steady_clock::now(); - [[nodiscard]] static llvm::raw_ostream & - getLogStream(std::optional Level, - const std::map, - std::variant> - &PassedLevelsToStreamVariant); - [[nodiscard]] static llvm::raw_ostream &getLogStreamFromStreamVariant( - const std::variant &StreamVariant); + static inline SeverityLevel LogFilterLevel = CRITICAL; }; #ifdef DYNAMIC_LOG // For performance reason, we want to disable any // formatting computation that would go straight into -// logs if logs are deactivated This macro does just +// logs if logs are deactivated. This macro does just // that #define IF_LOG_ENABLED_BOOL(condition, computation) \ if (LLVM_UNLIKELY(condition)) { \ computation; \ } -#define IS_LOG_ENABLED Logger::isLoggingEnabled() - +#define IS_LOG_ENABLED ::psr::Logger::isLoggingEnabled() #define IF_LOG_ENABLED(computation) \ - IF_LOG_ENABLED_BOOL(Logger::isLoggingEnabled(), computation) + IF_LOG_ENABLED_BOOL(::psr::Logger::isLoggingEnabled(), computation) + +#define IS_LOG_LEVEL_ENABLED(level) \ + (::psr::Logger::isLoggingEnabled() && \ + (::psr::SeverityLevel::level) >= ::psr::Logger::getLoggerFilterLevel()) +#define IF_LOG_LEVEL_ENABLED(level, computation) \ + IF_LOG_ENABLED_BOOL(IS_LOG_LEVEL_ENABLED(level), computation) #define PHASAR_LOG_LEVEL(level, message) \ - IF_LOG_ENABLED_BOOL( \ - Logger::isLoggingEnabled() && (level) >= Logger::getLoggerFilterLevel(), \ - do { \ - auto &S = Logger::getLogStream(level, std::nullopt); \ - Logger::addLinePrefix(S, level, std::nullopt); \ - /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ - S << message << '\n'; \ - } while (false);) + do { \ + IF_LOG_ENABLED_BOOL(IS_LOG_LEVEL_ENABLED(level), { \ + auto &Stream = ::psr::Logger::getLogStreamWithLinePrefix( \ + ::psr::SeverityLevel::level, std::nullopt); \ + /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + Stream << message << '\n'; \ + }) \ + } while (false) #define PHASAR_LOG(message) PHASAR_LOG_LEVEL(DEBUG, message) #define PHASAR_LOG_LEVEL_CAT(level, cat, message) \ - IF_LOG_ENABLED_BOOL( \ - Logger::isLoggingEnabled() && \ - (level) >= Logger::getLoggerFilterLevel() && \ - Logger::logCategory(cat, level), \ - do { \ - auto &S = Logger::getLogStream(level, cat); \ - Logger::addLinePrefix(S, level, cat); \ - /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ - S << message << '\n'; \ - } while (false);) + do { \ + IF_LOG_ENABLED_BOOL( \ + IS_LOG_LEVEL_ENABLED(level) && \ + ::psr::Logger::logCategory(cat, ::psr::SeverityLevel::level), \ + { \ + auto &Stream = ::psr::Logger::getLogStreamWithLinePrefix( \ + ::psr::SeverityLevel::level, cat); \ + /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + Stream << message << '\n'; \ + }) \ + } while (false) #define PHASAR_LOG_CAT(cat, message) \ - IF_LOG_ENABLED_BOOL( \ - Logger::isLoggingEnabled() && Logger::logCategory(cat, std::nullopt), \ - do { \ - auto &S = Logger::getLogStream(std::nullopt, cat); \ - Logger::addLinePrefix(S, std::nullopt, cat); \ - /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ - S << message << '\n'; \ - } while (false);) + do { \ + IF_LOG_ENABLED_BOOL(::psr::Logger::isLoggingEnabled() && \ + ::psr::Logger::logCategory(cat, std::nullopt), \ + { \ + auto &Stream = \ + ::psr::Logger::getLogStreamWithLinePrefix( \ + std::nullopt, cat); \ + /* NOLINTNEXTLINE(bugprone-macro-parentheses) */ \ + Stream << message << '\n'; \ + }) \ + } while (false) #else +#define IS_LOG_ENABLED false #define IF_LOG_ENABLED_BOOL(condition, computation) \ {} #define IF_LOG_ENABLED(computation) \ {} -#define PHASAR_LOG(computation) ((void)0) -#define PHASAR_LOG_CAT(cat, message) ((void)0) -#define PHASAR_LOG_LEVEL_CAT(level, cat, message) ((void)0) -#define PHASAR_LOG_LEVEL(level, message) ((void)0) -#define IS_LOG_ENABLED false +#define IS_LOG_LEVEL_ENABLED(level) false +#define IF_LOG_LEVEL_ENABLED(level, computation) \ + {} +#define PHASAR_LOG(computation) (void)0 +#define PHASAR_LOG_CAT(cat, message) (void)0 +#define PHASAR_LOG_LEVEL_CAT(level, cat, message) (void)0 +#define PHASAR_LOG_LEVEL(level, message) (void)0 #endif /** diff --git a/include/phasar/Utils/MaybeUniquePtr.h b/include/phasar/Utils/MaybeUniquePtr.h index 4a20f6e75..fe086fc85 100644 --- a/include/phasar/Utils/MaybeUniquePtr.h +++ b/include/phasar/Utils/MaybeUniquePtr.h @@ -11,10 +11,10 @@ #define PHASAR_UTILS_MAYBEUNIQUEPTR_H_ #include "llvm/ADT/PointerIntPair.h" +#include "llvm/Support/PointerLikeTypeTraits.h" #include #include -#include namespace psr { @@ -26,156 +26,177 @@ template class MaybeUniquePtrBase { bool Flag = false; /// Compatibility with llvm::PointerIntPair: - [[nodiscard]] T *getPointer() const noexcept { return Pointer; } - [[nodiscard]] bool getInt() const noexcept { return Flag; } - void setInt(bool Flag) noexcept { this->Flag = Flag; } + [[nodiscard]] constexpr T *getPointer() const noexcept { return Pointer; } + [[nodiscard]] constexpr bool getInt() const noexcept { return Flag; } + constexpr void setInt(bool Flag) noexcept { this->Flag = Flag; } }; std::conditional_t<(alignof(T) > 1), llvm::PointerIntPair, PointerBoolPairFallback> Data{}; - MaybeUniquePtrBase(T *Ptr, bool Owns) noexcept : Data{Ptr, Owns} {} - MaybeUniquePtrBase() noexcept = default; + constexpr MaybeUniquePtrBase(T *Ptr, bool Owns) noexcept : Data{Ptr, Owns} {} + constexpr MaybeUniquePtrBase() noexcept = default; }; template class MaybeUniquePtrBase { + struct PointerTraits : llvm::PointerLikeTypeTraits { + static constexpr int NumLowBitsAvailable = 1; + }; + protected: - llvm::PointerIntPair Data{}; + llvm::PointerIntPair Data{}; - MaybeUniquePtrBase(T *Ptr, bool Owns) noexcept : Data{Ptr, Owns} { - static_assert(alignof(T) > 1, - "Using MaybeUniquePtr requires alignment > 1!"); - } - MaybeUniquePtrBase() noexcept = default; + constexpr MaybeUniquePtrBase(T *Ptr, bool Owns) noexcept : Data{Ptr, Owns} {} + constexpr MaybeUniquePtrBase() noexcept = default; }; } // namespace detail -/// A smart-pointer, similar to std::unique_ptr that can be used as both, -/// owning and non-owning pointer. +/// A smart-pointer, similar to std::unique_ptr, that can optionally own an +/// object. /// /// \tparam T The pointee type -/// \tparam RequireAlignment If true, the datastructure only works if alignof(T) -/// > 1 holds. Enables incomplete T types +/// \tparam RequireAlignment If true, the datastructure only works if +/// alignof(T) > 1 holds. Enables incomplete T types template class MaybeUniquePtr : detail::MaybeUniquePtrBase { using detail::MaybeUniquePtrBase::Data; public: - MaybeUniquePtr() noexcept = default; + constexpr MaybeUniquePtr() noexcept = default; - MaybeUniquePtr(T *Pointer, bool Owns = false) noexcept - : detail::MaybeUniquePtrBase( - Pointer, Owns && Pointer != nullptr) {} + constexpr MaybeUniquePtr(T *Pointer, bool Owns = false) noexcept + : detail::MaybeUniquePtrBase(Pointer, + Owns && Pointer) {} - MaybeUniquePtr(std::unique_ptr &&Owner) noexcept + constexpr MaybeUniquePtr(std::unique_ptr &&Owner) noexcept : MaybeUniquePtr(Owner.release(), true) {} - template - MaybeUniquePtr(std::unique_ptr &&Owner) noexcept + template && + std::is_convertible_v>> + constexpr MaybeUniquePtr(std::unique_ptr &&Owner) noexcept : MaybeUniquePtr(Owner.release(), true) {} - MaybeUniquePtr(MaybeUniquePtr &&Other) noexcept + constexpr MaybeUniquePtr(MaybeUniquePtr &&Other) noexcept : detail::MaybeUniquePtrBase( std::exchange(Other.Data, {})) {} - void swap(MaybeUniquePtr &Other) noexcept { std::swap(Data, Other, Data); } + constexpr void swap(MaybeUniquePtr &Other) noexcept { + std::swap(Data, Other.Data); + } - friend void swap(MaybeUniquePtr &LHS, MaybeUniquePtr &RHS) noexcept { + constexpr friend void swap(MaybeUniquePtr &LHS, + MaybeUniquePtr &RHS) noexcept { LHS.swap(RHS); } - MaybeUniquePtr &operator=(MaybeUniquePtr &&Other) noexcept { + constexpr MaybeUniquePtr &operator=(MaybeUniquePtr &&Other) noexcept { swap(Other); return *this; } - MaybeUniquePtr &operator=(std::unique_ptr &&Owner) noexcept { + constexpr MaybeUniquePtr &operator=(std::unique_ptr &&Owner) noexcept { if (owns()) { delete Data.getPointer(); } - Data = {Owner.release(), true}; + auto *Ptr = Owner.release(); + Data = {Ptr, Ptr != nullptr}; return *this; } - template - MaybeUniquePtr &operator=(std::unique_ptr &&Owner) noexcept { + template && + std::is_convertible_v>> + constexpr MaybeUniquePtr &operator=(std::unique_ptr &&Owner) noexcept { if (owns()) { delete Data.getPointer(); } - Data = {Owner.release(), true}; + auto *Ptr = Owner.release(); + Data = {Ptr, Ptr != nullptr}; return *this; } MaybeUniquePtr(const MaybeUniquePtr &) = delete; MaybeUniquePtr &operator=(const MaybeUniquePtr &) = delete; - ~MaybeUniquePtr() { +#if __cplusplus >= 202002L + constexpr +#endif + ~MaybeUniquePtr() { if (owns()) { delete Data.getPointer(); Data = {}; } } - [[nodiscard]] T *get() noexcept { return Data.getPointer(); } - [[nodiscard]] const T *get() const noexcept { return Data.getPointer(); } + [[nodiscard]] constexpr T *get() const noexcept { return Data.getPointer(); } - [[nodiscard]] T *operator->() noexcept { return get(); } - [[nodiscard]] const T *operator->() const noexcept { return get(); } + [[nodiscard]] constexpr T *operator->() const noexcept { return get(); } - [[nodiscard]] T &operator*() noexcept { - assert(get() != nullptr); - return *get(); - } - [[nodiscard]] const T &operator*() const noexcept { + [[nodiscard]] constexpr T &operator*() const noexcept { assert(get() != nullptr); return *get(); } - T *release() noexcept { + constexpr T *release() noexcept { Data.setInt(false); return Data.getPointer(); } - void reset() noexcept { + constexpr void reset() noexcept { if (owns()) { delete Data.getPointer(); } Data = {}; } - [[nodiscard]] bool owns() const noexcept { - return Data.getInt() && Data.getPointer(); + template + constexpr void reset(MaybeUniquePtr &&Other) noexcept { + *this = std::move(Other); + } + + template + constexpr void reset(std::unique_ptr &&Other) noexcept { + *this = std::move(Other); + } + + [[nodiscard]] constexpr bool owns() const noexcept { + assert(Data.getPointer() || !Data.getInt()); + return Data.getInt(); } - friend bool operator==(const MaybeUniquePtr &LHS, - const MaybeUniquePtr &RHS) noexcept { + constexpr friend bool operator==(const MaybeUniquePtr &LHS, + const MaybeUniquePtr &RHS) noexcept { return LHS.Data.getPointer() == RHS.Data.getPointer(); } - friend bool operator!=(const MaybeUniquePtr &LHS, - const MaybeUniquePtr &RHS) noexcept { + constexpr friend bool operator!=(const MaybeUniquePtr &LHS, + const MaybeUniquePtr &RHS) noexcept { return !(LHS == RHS); } - friend bool operator==(const MaybeUniquePtr &LHS, const T *RHS) noexcept { + constexpr friend bool operator==(const MaybeUniquePtr &LHS, + const T *RHS) noexcept { return LHS.Data.getPointer() == RHS; } - friend bool operator!=(const MaybeUniquePtr &LHS, const T *RHS) noexcept { + constexpr friend bool operator!=(const MaybeUniquePtr &LHS, + const T *RHS) noexcept { return !(LHS == RHS); } - friend bool operator==(const T *LHS, const MaybeUniquePtr &RHS) noexcept { + constexpr friend bool operator==(const T *LHS, + const MaybeUniquePtr &RHS) noexcept { return LHS == RHS.Data.getPointer(); } - friend bool operator!=(const T *LHS, const MaybeUniquePtr &RHS) noexcept { + constexpr friend bool operator!=(const T *LHS, + const MaybeUniquePtr &RHS) noexcept { return !(LHS == RHS); } - explicit operator bool() const noexcept { + constexpr explicit operator bool() const noexcept { return Data.getPointer() != nullptr; } }; - } // namespace psr #endif // PHASAR_UTILS_MAYBEUNIQUEPTR_H_ diff --git a/include/phasar/Utils/NullAnalysisPrinter.h b/include/phasar/Utils/NullAnalysisPrinter.h new file mode 100644 index 000000000..8527cc3c8 --- /dev/null +++ b/include/phasar/Utils/NullAnalysisPrinter.h @@ -0,0 +1,28 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_NULLANALYSISPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_NULLANALYSISPRINTER_H + +#include "phasar/Utils/AnalysisPrinterBase.h" + +namespace psr { + +template +class NullAnalysisPrinter final : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + static NullAnalysisPrinter *getInstance() { + static auto Instance = NullAnalysisPrinter(); + return &Instance; + } + +private: + void doOnResult(n_t /*Instr*/, d_t /*DfFact*/, l_t /*Lattice*/, + DataFlowAnalysisType /*AnalysisType*/) override{}; + + NullAnalysisPrinter() = default; +}; + +} // namespace psr +#endif diff --git a/include/phasar/Utils/OnTheFlyAnalysisPrinter.h b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h new file mode 100644 index 000000000..aa3e4ef23 --- /dev/null +++ b/include/phasar/Utils/OnTheFlyAnalysisPrinter.h @@ -0,0 +1,49 @@ +#ifndef PHASAR_PHASARLLVM_UTILS_ONTHEFLYANALYSISPRINTER_H +#define PHASAR_PHASARLLVM_UTILS_ONTHEFLYANALYSISPRINTER_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/AnalysisPrinterBase.h" +#include "phasar/Utils/IO.h" +#include "phasar/Utils/MaybeUniquePtr.h" +#include "phasar/Utils/Printer.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/raw_ostream.h" + +#include +namespace psr { + +template +class OnTheFlyAnalysisPrinter : public AnalysisPrinterBase { + using n_t = typename AnalysisDomainTy::n_t; + using d_t = typename AnalysisDomainTy::d_t; + using l_t = typename AnalysisDomainTy::l_t; + +public: + explicit OnTheFlyAnalysisPrinter(llvm::raw_ostream &OS) + : AnalysisPrinterBase(), OS(&OS){}; + + explicit OnTheFlyAnalysisPrinter(const llvm::Twine &Filename) + : AnalysisPrinterBase(), OS(openFileStream(Filename)){}; + + OnTheFlyAnalysisPrinter() = default; + ~OnTheFlyAnalysisPrinter() = default; + + [[nodiscard]] bool isValid() const noexcept { return OS != nullptr; } + +private: + void doOnResult(n_t Instr, d_t DfFact, l_t LatticeElement, + DataFlowAnalysisType /*AnalysisType*/) override { + assert(isValid()); + *OS << "\nAt IR statement: " << NToString(Instr) << "\n"; + *OS << "\tFact: " << DToString(DfFact) << "\n"; + if constexpr (!std::is_same_v) { + *OS << "Value: " << LToString(LatticeElement) << "\n"; + } + } + + MaybeUniquePtr OS = nullptr; +}; +} // namespace psr + +#endif diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 2f9265516..50f8f215c 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -17,12 +17,18 @@ #ifndef PHASAR_UTILS_PAMM_H_ #define PHASAR_UTILS_PAMM_H_ +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + #include // high_resolution_clock::time_point, milliseconds +#include #include -#include // set -#include // string -#include // unordered_map -#include // vector +#include // set +#include // string +#include // vector namespace llvm { class raw_ostream; @@ -50,24 +56,13 @@ namespace psr { /// @note This class implements the Singleton Pattern - use the /// PAMM_GET_INSTANCE macro to retrieve an instance of PAMM before you use any /// other macro from this class. -class PAMM { -private: - PAMM() = default; - ~PAMM() = default; +class PAMM final { +public: using TimePoint_t = std::chrono::high_resolution_clock::time_point; using Duration_t = std::chrono::milliseconds; - std::unordered_map RunningTimer; - std::unordered_map> - StoppedTimer; - std::unordered_map>> - RepeatingTimer; - std::unordered_map Counter; - std::unordered_map> - Histogram; -public: + PAMM() noexcept = default; + ~PAMM() = default; // PAMM is used as singleton. PAMM(const PAMM &PM) = delete; PAMM(PAMM &&PM) = delete; @@ -76,7 +71,7 @@ class PAMM { /// \brief Returns a reference to the PAMM object (singleton) - associated /// macro: PAMM_GET_INSTANCE. - static PAMM &getInstance(); + [[nodiscard]] static PAMM &getInstance(); /// \brief Resets PAMM, i.e. discards all gathered information (timer, counter /// etc.) - associated macro: RESET_PAMM. @@ -86,12 +81,12 @@ class PAMM { /// \brief Starts a timer under the given timer id - associated macro: /// START_TIMER(TIMER_ID, SEV_LVL). /// \param TimerId Unique timer id. - void startTimer(const std::string &TimerId); + void startTimer(llvm::StringRef TimerId); /// \brief Resets timer under the given timer id - associated macro: /// RESET_TIMER(TIMER_ID, SEV_LVL). /// \param TimerId Unique timer id. - void resetTimer(const std::string &TimerId); + void resetTimer(llvm::StringRef TimerId); /// If pauseTimer is true, a running timer gets paused, its start time point /// will paired with a current time point, and stored as an accumulated timer. @@ -107,18 +102,18 @@ class PAMM { /// \brief Stops or pauses a timer under the given timer id. /// \param TimerId Unique timer id. /// \param PauseTimer If true, timer will be paused instead of stopped. - void stopTimer(const std::string &TimerId, bool PauseTimer = false); + void stopTimer(llvm::StringRef TimerId, bool PauseTimer = false); /// \brief Computes the elapsed time of the given timer up until now or up to /// the moment the timer was stopped - associated macro: GET_TIMER(TIMERID) /// \param TimerId Unique timer id. /// \return Timer duration. - unsigned long elapsedTime(const std::string &TimerId); + uint64_t elapsedTime(llvm::StringRef TimerId); /// For each accumulated timer a vector holds all recorded durations. /// \brief Computes the elapsed time for all accumulated timer being used. /// \return Map containing measured durations of all accumulated timer. - std::unordered_map> + [[nodiscard]] llvm::StringMap> elapsedTimeOfRepeatingTimer(); /// A running timer will not be stopped. The precision for time computation @@ -129,42 +124,46 @@ class PAMM { /// explicitly. /// \brief Returns the elapsed time for a given timer id. /// \param timerId Unique timer id. - static std::string getPrintableDuration(unsigned long Duration); + [[nodiscard]] static std::string getPrintableDuration(uint64_t Duration); /// \brief Registers a new counter under the given counter id - associated /// macro: REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL). /// \param CounterId Unique counter id. - void regCounter(const std::string &CounterId, unsigned IntialValue = 0); + void regCounter(llvm::StringRef CounterId, unsigned IntialValue = 0); /// \brief Increases the count for the given counter - associated macro: /// INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). /// \param CounterId Unique counter id. /// \param CValue to be added to the current counter. - void incCounter(const std::string &CounterId, unsigned CValue = 1); + void incCounter(llvm::StringRef CounterId, unsigned CValue = 1); /// \brief Decreases the count for the given counter - associated macro: /// DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL). /// \param CounterId Unique counter id. /// \param CValue to be subtracted from the current counter. - void decCounter(const std::string &CounterId, unsigned CValue = 1); + void decCounter(llvm::StringRef CounterId, unsigned CValue = 1); /// The associated macro does not check PAMM's severity level explicitly. /// \brief Returns the current count for the given counter - associated macro: /// GET_COUNTER(COUNTER_ID). /// \param CounterId Unique counter id. - int getCounter(const std::string &CounterId); + std::optional getCounter(llvm::StringRef CounterId); /// The associated macro does not check PAMM's severity level explicitly. /// \brief Sums the counts for the given counter ids - associated macro: /// GET_SUM_COUNT(...). /// \param CounterIds Unique counter ids. /// \note Macro uses variadic parameters, e.g. GET_SUM_COUNT({"foo", "bar"}). - int getSumCount(const std::set &CounterIds); + std::optional getSumCount(const std::set &CounterIds); + std::optional + getSumCount(llvm::ArrayRef CounterIds); + std::optional + getSumCount(std::initializer_list CounterIds); /// \brief Registers a new histogram - associated macro: /// REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL). /// \param HistogramId Unique hitogram id. - void regHistogram(const std::string &HistogramId); + void regHistogram(llvm::StringRef HistogramId); /// \brief Adds a new observed data point to the corresponding histogram - /// associated macro: ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, @@ -172,9 +171,10 @@ class PAMM { /// \param HistogramId ID of the histogram that tracks given data points. /// \param DataPointId ID of the given data point. /// \param DataPointValue Value of the given data point. - void addToHistogram(const std::string &HistogramId, - const std::string &DataPointId, - unsigned long DataPointValue = 1); + void addToHistogram(llvm::StringRef HistogramId, llvm::StringRef DataPointId, + uint64_t DataPointValue = 1); + + void stopAllTimers(); void printTimers(llvm::raw_ostream &OS); @@ -190,11 +190,20 @@ class PAMM { /// EXPORT_MEASURED_DATA(PATH). /// \param OutputPath to exported JSON file. void exportMeasuredData( - const std::string &OutputPath, - const std::string &ProjectId = "default-phasar-project", - const std::optional> &Modules = std::nullopt, - const std::optional> &DataFlowAnalyses = - std::nullopt); + const llvm::Twine &OutputPath, + llvm::StringRef ProjectId = "default-phasar-project", + const std::vector *Modules = nullptr, + const std::vector *DataFlowAnalyses = nullptr); + + [[nodiscard]] const auto &getHistogram() const noexcept { return Histogram; } + +private: + llvm::StringMap RunningTimer; + llvm::StringMap> StoppedTimer; + llvm::StringMap>> + RepeatingTimer; + llvm::StringMap Counter; + llvm::StringMap> Histogram; }; } // namespace psr diff --git a/include/phasar/Utils/PAMMMacros.h b/include/phasar/Utils/PAMMMacros.h index adf4356c1..cc74e6631 100644 --- a/include/phasar/Utils/PAMMMacros.h +++ b/include/phasar/Utils/PAMMMacros.h @@ -17,16 +17,24 @@ #ifndef PHASAR_UTILS_PAMMMACROS_H_ #define PHASAR_UTILS_PAMMMACROS_H_ +#include "phasar/Config/phasar-config.h" + namespace psr { /// Defines the different level of severity of PAMM's performance evaluation -enum PAMM_SEVERITY_LEVEL { Off = 0, Core, Full }; // NOLINT +enum class PAMM_SEVERITY_LEVEL { Off = 0, Core, Full }; // NOLINT #if defined(PAMM_FULL) -static constexpr unsigned PAMM_CURR_SEV_LEVEL = 2; // NOLINT +// NOLINTNEXTLINE +static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = + PAMM_SEVERITY_LEVEL::Full; #elif defined(PAMM_CORE) -static constexpr unsigned PAMM_CURR_SEV_LEVEL = 1; // NOLINT +// NOLINTNEXTLINE +static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = + PAMM_SEVERITY_LEVEL::Core; #else -static constexpr unsigned PAMM_CURR_SEV_LEVEL = 0; // NOLINT +// NOLINTNEXTLINE +static constexpr PAMM_SEVERITY_LEVEL PAMM_CURR_SEV_LEVEL = + PAMM_SEVERITY_LEVEL::Off; #endif } // namespace psr @@ -39,46 +47,46 @@ static constexpr unsigned PAMM_CURR_SEV_LEVEL = 0; // NOLINT #define PAMM_RESET pamm.reset() #define START_TIMER(TIMER_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.startTimer(TIMER_ID); \ } #define RESET_TIMER(TIMER_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.resetTimer(TIMER_ID); \ } #define PAUSE_TIMER(TIMER_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.stopTimer(TIMER_ID, true); \ } #define STOP_TIMER(TIMER_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.stopTimer(TIMER_ID); \ } #define PRINT_TIMER(TIMER_ID) \ pamm.getPrintableDuration(pamm.elapsedTime(TIMER_ID)) #define REG_COUNTER(COUNTER_ID, INIT_VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.regCounter(COUNTER_ID, INIT_VALUE); \ } #define INC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.incCounter(COUNTER_ID, VALUE); \ } #define DEC_COUNTER(COUNTER_ID, VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.decCounter(COUNTER_ID, VALUE); \ } #define GET_COUNTER(COUNTER_ID) pamm.getCounter(COUNTER_ID) #define GET_SUM_COUNT(...) pamm.getSumCount(__VA_ARGS__) #define REG_HISTOGRAM(HISTOGRAM_ID, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ pamm.regHistogram(HISTOGRAM_ID); \ } #define ADD_TO_HISTOGRAM(HISTOGRAM_ID, DATAPOINT_ID, DATAPOINT_VALUE, SEV_LVL) \ - if constexpr (PAMM_CURR_SEV_LEVEL >= SEV_LVL) { \ - pamm.addToHistogram(HISTOGRAM_ID, std::to_string(DATAPOINT_ID), \ + if constexpr (PAMM_CURR_SEV_LEVEL >= PAMM_SEVERITY_LEVEL::SEV_LVL) { \ + pamm.addToHistogram(HISTOGRAM_ID, adl_to_string(DATAPOINT_ID), \ DATAPOINT_VALUE); \ } @@ -101,9 +109,10 @@ static constexpr unsigned PAMM_CURR_SEV_LEVEL = 0; // NOLINT #define EXPORT_MEASURED_DATA(PATH) // The following macros could be used in log messages, thus they have to // provide some default value to avoid compiler errors -#define PRINT_TIMER(TIMER_ID) "-1" -#define GET_COUNTER(COUNTER_ID) "-1" -#define GET_SUM_COUNT(...) "-1" +#define PRINT_TIMER(TIMER_ID) "" +#define GET_COUNTER(COUNTER_ID) "" +#define GET_SUM_COUNT(...) "" + #endif #endif diff --git a/include/phasar/Utils/Printer.h b/include/phasar/Utils/Printer.h index c30e21401..31a08cdb4 100644 --- a/include/phasar/Utils/Printer.h +++ b/include/phasar/Utils/Printer.h @@ -7,106 +7,112 @@ * Philipp Schubert and others *****************************************************************************/ -#ifndef PHASAR_PHASARLLVM_UTILS_PRINTER_H -#define PHASAR_PHASARLLVM_UTILS_PRINTER_H +#ifndef PHASAR_UTILS_PRINTER_H +#define PHASAR_UTILS_PRINTER_H +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" -#include #include +#include -namespace psr { - -template -std::string toStringBuilder(void (P::*Printer)(llvm::raw_ostream &, T) const, - const P *This, const T &Arg) { - std::string Buffer; - llvm::raw_string_ostream StrS(Buffer); - std::invoke(Printer, This, std::ref(StrS), Arg); - return Buffer; -} - -template struct NodePrinterBase { - virtual ~NodePrinterBase() = default; - - virtual void printNode(llvm::raw_ostream &OS, N Stmt) const = 0; - - [[nodiscard]] std::string NtoString(N Stmt) const { // NOLINT - return toStringBuilder(&NodePrinterBase::printNode, this, Stmt); - } -}; -template -using NodePrinter = NodePrinterBase; +namespace llvm { +class Value; +class Instruction; +class Function; +} // namespace llvm -template struct DataFlowFactPrinterBase { - virtual ~DataFlowFactPrinterBase() = default; - - virtual void printDataFlowFact(llvm::raw_ostream &OS, D Fact) const = 0; - - [[nodiscard]] std::string DtoString(D Fact) const { // NOLINT - return toStringBuilder(&DataFlowFactPrinterBase::printDataFlowFact, this, - Fact); - } -}; -template -using DataFlowFactPrinter = - DataFlowFactPrinterBase; - -template struct ValuePrinter { - virtual ~ValuePrinter() = default; - - virtual void printValue(llvm::raw_ostream &OS, V Val) const = 0; - - [[nodiscard]] std::string VtoString(V Val) const { // NOLINT - return toStringBuilder(&ValuePrinter::printValue, this, Val); - } -}; - -template struct TypePrinter { - virtual ~TypePrinter() = default; - - virtual void printType(llvm::raw_ostream &OS, T Ty) const = 0; - - [[nodiscard]] std::string TtoString(T Ty) const { // NOLINT - return toStringBuilder(&TypePrinter::printType, this, Ty); +namespace psr { +namespace detail { +template +static constexpr bool IsSomehowPrintable = + has_str_v || is_llvm_printable_v || has_adl_to_string_v; + +template decltype(auto) printSomehow(const T &Val) { + if constexpr (has_str_v) { + return Val.str(); + } else if constexpr (has_adl_to_string_v) { + return adl_to_string(Val); + } else if constexpr (is_llvm_printable_v) { + std::string Str; + llvm::raw_string_ostream ROS(Str); + ROS << Val; + return Str; + } else { + llvm_unreachable( + "All compilable cases should be handled in the if-chain above"); } -}; - -template struct EdgeFactPrinter { - using l_t = typename AnalysisDomainTy::l_t; - - virtual ~EdgeFactPrinter() = default; +} +} // namespace detail + +/// Stringify the given ICFG node (instruction/statement). +/// +/// Default implementation. Provide your own overload to customize this API for +/// your types +template >> +[[nodiscard]] decltype(auto) NToString(const N &Node) { + return detail::printSomehow(Node); +} - virtual void printEdgeFact(llvm::raw_ostream &OS, l_t L) const = 0; +/// Stringify the given data-flow fact. +/// +/// Default implementation. Provide your own overload to customize this API for +/// your types +template >> +[[nodiscard]] decltype(auto) DToString(const D &Fact) { + return detail::printSomehow(Fact); +} - [[nodiscard]] std::string LtoString(l_t L) const { // NOLINT - return toStringBuilder(&EdgeFactPrinter::printEdgeFact, this, L); - } -}; +/// Stringify the given edge value. +/// +/// Default implementation. Provide your own overload to customize this API for +/// your types +template >> +[[nodiscard]] decltype(auto) LToString(const L &Value) { + return detail::printSomehow(Value); +} -template struct FunctionPrinter { - using F = typename AnalysisDomainTy::f_t; +/// Stringify the given function. +/// +/// Default implementation. Provide your own overload to customize this API for +/// your types +template >> +[[nodiscard]] std::string FToString(const F &Fun) { + return detail::printSomehow(Fun); +} - virtual ~FunctionPrinter() = default; +// -- specializations - virtual void printFunction(llvm::raw_ostream &OS, F Func) const = 0; +// --- LLVM +// Note: Provide forward declarations here, such that improper usage will +// definitely lead to an error instead of triggering one of the default +// implementations - [[nodiscard]] std::string FtoString(F Func) const { // NOLINT - return toStringBuilder(&FunctionPrinter::printFunction, this, Func); - } -}; +/// Stringify the given LLVM Value. +/// +/// \remark Link phasar_llvm_utils to use this +[[nodiscard]] std::string NToString(const llvm::Value *V); -template struct ContainerPrinter { - virtual ~ContainerPrinter() = default; +/// Stringify the given LLVM Instruction. +/// +/// \remark Link phasar_llvm_utils to use this +[[nodiscard]] std::string NToString(const llvm::Instruction *V); - virtual void printContainer(llvm::raw_ostream &OS, - ContainerTy Container) const = 0; +/// Stringify the given LLVM Value. +/// +/// \remark Link phasar_llvm_utils to use this +[[nodiscard]] std::string DToString(const llvm::Value *V); - [[nodiscard]] std::string - ContainertoString(ContainerTy Container) const { // NOLINT - return toStringBuilder(&ContainerPrinter::printContainer, this, Container); - } -}; +/// Stringify the given LLVM Function. +/// +/// \remark Link phasar_llvm_utils to use this +[[nodiscard]] llvm::StringRef FToString(const llvm::Function *V); } // namespace psr diff --git a/include/phasar/Utils/RepeatIterator.h b/include/phasar/Utils/RepeatIterator.h new file mode 100644 index 000000000..94245930a --- /dev/null +++ b/include/phasar/Utils/RepeatIterator.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * Copyright (c) 2022 Philipp Schubert. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel + *****************************************************************************/ + +#ifndef PHASAR_UTILS_REPEATITERATOR_H +#define PHASAR_UTILS_REPEATITERATOR_H + +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/iterator_range.h" + +#include +#include +#include +#include + +namespace psr { +/// An iterator that iterates over the same value a specified number of times +template class RepeatIterator { +public: + using value_type = T; + using reference = const T &; + using pointer = const T *; + using difference_type = ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + reference operator*() const noexcept { + assert(Elem.has_value() && "Dereferencing end()-iterator"); + return *Elem; + } + pointer operator->() const noexcept { + assert(Elem.has_value() && "Dereferencing end()-iterator"); + return &*Elem; + } + + RepeatIterator &operator++() noexcept { + ++Index; + return *this; + } + RepeatIterator operator++(int) noexcept { + auto Ret = *this; + ++*this; + return Ret; + } + + bool operator==(const RepeatIterator &Other) const noexcept { + return Other.Index == Index; + } + bool operator!=(const RepeatIterator &Other) const noexcept { + return !(*this == Other); + } + + template >>> + explicit RepeatIterator(TT &&Elem) : Elem(std::forward(Elem)) {} + explicit RepeatIterator(size_t Index, std::true_type /*AsEndIterator*/) + : Index(Index), Elem(std::nullopt) {} + + RepeatIterator() noexcept = default; + +private: + size_t Index{}; + std::optional Elem{}; +}; + +template +using RepeatRangeType = llvm::iterator_range>; +template auto repeat(T &&Elem, size_t Num) { + using iterator_type = RepeatIterator>; + auto Ret = llvm::make_range(iterator_type(std::forward(Elem)), + iterator_type(Num, std::true_type{})); + return Ret; +} + +static_assert(is_iterable_over_v, int>); + +} // namespace psr + +#endif // PHASAR_UTILS_REPEATITERATOR_H diff --git a/include/phasar/Utils/StableVector.h b/include/phasar/Utils/StableVector.h index f89ed5ab9..12c18853e 100644 --- a/include/phasar/Utils/StableVector.h +++ b/include/phasar/Utils/StableVector.h @@ -11,6 +11,7 @@ #define PHASAR_UTILS_STABLEVECTOR_H_ #include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/raw_ostream.h" @@ -20,6 +21,7 @@ #include #include #include +#include namespace psr { @@ -152,20 +154,21 @@ class StableVector { using allocator_type = typename std::allocator_traits::template rebind_alloc; - StableVector(const allocator_type &Alloc = allocator_type()) noexcept( + StableVector() noexcept( + std::is_nothrow_default_constructible_v) = default; + + StableVector(const allocator_type &Alloc) noexcept( std::is_nothrow_copy_constructible_v) : Alloc(Alloc) {} StableVector(StableVector &&Other) noexcept - : Blocks(std::move(Other.Blocks)), Start(Other.Start), Pos(Other.Pos), - End(Other.End), Size(Other.Size), BlockIdx(Other.BlockIdx), - Alloc(std::move(Other.Alloc)) { - Other.Start = nullptr; - Other.Pos = nullptr; - Other.End = nullptr; - Other.Size = 0; - Other.BlockIdx = 0; - } + : Blocks(std::move(Other.Blocks)), + Start(std::exchange(Other.Start, nullptr)), + Pos(std::exchange(Other.Pos, nullptr)), + End(std::exchange(Other.End, nullptr)), + Size(std::exchange(Other.Size, 0)), + BlockIdx(std::exchange(Other.BlockIdx, 0)), + Alloc(std::move(Other.Alloc)) {} explicit StableVector(const StableVector &Other) : Size(Other.Size), BlockIdx(Other.BlockIdx), @@ -197,35 +200,36 @@ class StableVector { Start = Blck; End = Blck + Cap; Pos = Blck + (Other.Pos - Other.Start); + + __asan_poison_memory_region(Pos, (End - Pos) * sizeof(T)); } - friend void swap(StableVector &LHS, StableVector &RHS) noexcept { - std::swap(LHS.Blocks, RHS.Blocks); - std::swap(LHS.Start, RHS.Start); - std::swap(LHS.Pos, RHS.Pos); - std::swap(LHS.End, RHS.End); - std::swap(LHS.Size, RHS.Size); - std::swap(LHS.BlockIdx, RHS.BlockIdx); + void swap(StableVector &Other) noexcept { + std::swap(Blocks, Other.Blocks); + std::swap(Start, Other.Start); + std::swap(Pos, Other.Pos); + std::swap(End, Other.End); + std::swap(Size, Other.Size); + std::swap(BlockIdx, Other.BlockIdx); if constexpr (std::allocator_traits< allocator_type>::propagate_on_container_swap::value) { - std::swap(LHS.Alloc, RHS.Alloc); + std::swap(Alloc, Other.Alloc); } else { - assert(LHS.Alloc == RHS.Alloc && + assert(Alloc == Other.Alloc && "Do not swap two StableVectors with incompatible " "allocators that do not propagate on swap!"); } } - - void swap(StableVector &Other) noexcept { swap(*this, Other); } - - StableVector &operator=(StableVector Other) noexcept { - swap(*this, Other); - return *this; + friend void swap(StableVector &LHS, StableVector &RHS) noexcept { + LHS.swap(RHS); } + // This would be silently expensive... If you really want this, call clone() + StableVector &operator=(const StableVector &) = delete; + StableVector &operator=(StableVector &&Other) noexcept { - swap(*this, Other); + swap(Other); return *this; } @@ -245,6 +249,7 @@ class StableVector { std::destroy(Start, Pos); for (size_t I = BlockIdx; I < Blocks.size(); ++I) { + __asan_unpoison_memory_region(Blocks[I], Cap * sizeof(T)); std::allocator_traits::deallocate(Alloc, Blocks[I], Cap); Cap = TotalSize; @@ -264,6 +269,7 @@ class StableVector { } auto Ret = Pos; + __asan_unpoison_memory_region(Ret, sizeof(T)); std::allocator_traits::construct( Alloc, Ret, std::forward(Args)...); ++Pos; @@ -344,6 +350,8 @@ class StableVector { assert(!empty() && "Do not call pop_back() on an empty StableVector!"); std::destroy_at(--Pos); + __asan_poison_memory_region(Pos, sizeof(T)); + --Size; if (Pos != Start) { return; @@ -375,11 +383,13 @@ class StableVector { for (size_t I = 0; I < BlockIdx; ++I) { std::destroy_n(Blocks[I], Cap); + __asan_poison_memory_region(Blocks[I], Cap * sizeof(T)); Cap = TotalSize; TotalSize += Cap; } std::destroy(Start, Pos); + __asan_poison_memory_region(Start, (Pos - Start) * sizeof(T)); BlockIdx = 0; Size = 0; if (!Blocks.empty()) { @@ -400,10 +410,12 @@ class StableVector { Pos -= N; Size -= N; std::destroy_n(Pos, N); + __asan_poison_memory_region(Pos, N * sizeof(T)); return; } std::destroy(Start, Pos); + __asan_poison_memory_region(Start, (Pos - Start) * sizeof(T)); Size -= NumElementsInCurrBlock; N -= NumElementsInCurrBlock; @@ -430,6 +442,7 @@ class StableVector { if (Size == 0) { assert(BlockIdx == 0); + __asan_unpoison_memory_region(Blocks[0], InitialCapacity * sizeof(T)); std::allocator_traits::deallocate(Alloc, Blocks[0], InitialCapacity); } @@ -438,6 +451,7 @@ class StableVector { for (size_t I = BlockIdx + 1, BlocksEnd = Blocks.size(); I < BlocksEnd; ++I) { + __asan_unpoison_memory_region(Blocks[I], Cap * sizeof(T)); std::allocator_traits::deallocate(Alloc, Blocks[I], Cap); Cap <<= 1; } @@ -458,24 +472,16 @@ class StableVector { size_t Total = InitialCapacity; for (size_t I = 0; I < LHS.BlockIdx; ++I) { - for (T *LIt = LHS.Blocks[I], *RIt = RHS.Blocks[I], *LEnd = LIt + Cap; - LIt != LEnd; ++LIt, ++RIt) { - if (*LIt != *RIt) { - return false; - } + auto LIt = LHS.Blocks[I]; + if (!std::equal(LIt, LIt + Cap, RHS.Blocks[I])) { + return false; } Cap = Total; Total <<= 1; } - for (T *LIt = LHS.Start, *RIt = RHS.Start, *LEnd = LHS.Pos; LIt != LEnd; - ++LIt, ++RIt) { - if (*LIt != *RIt) { - return false; - } - } - return true; + return std::equal(LHS.Start, LHS.Pos, RHS.Start); } [[nodiscard]] friend bool operator!=(const StableVector &LHS, @@ -492,7 +498,9 @@ class StableVector { template [[nodiscard]] T &growAndEmplace(ArgTys &&...Args) { auto makeBlock = [this](size_t N) { - return std::allocator_traits::allocate(Alloc, N); + auto *Ret = std::allocator_traits::allocate(Alloc, N); + __asan_poison_memory_region(std::next(Ret), (N - 1) * sizeof(T)); + return Ret; }; if (Blocks.empty()) { @@ -502,6 +510,7 @@ class StableVector { assert(llvm::isPowerOf2_64(Size)); BlockIdx++; End = Blocks[BlockIdx] + Size; + __asan_unpoison_memory_region(Blocks[BlockIdx], sizeof(T)); } else { assert(llvm::isPowerOf2_64(Size)); BlockIdx++; @@ -509,7 +518,7 @@ class StableVector { End = Blocks.back() + Size; } - auto Ret = Blocks[BlockIdx]; + auto *Ret = Blocks[BlockIdx]; Start = Ret; Pos = Ret + 1; @@ -538,13 +547,13 @@ class StableVector { // return Blocks[LogIdx][Offset]; } - llvm::SmallVector Blocks; + llvm::SmallVector Blocks{}; T *Start = nullptr; T *Pos = nullptr; T *End = nullptr; size_t Size = 0; size_t BlockIdx = 0; - [[no_unique_address]] allocator_type Alloc; + [[no_unique_address]] allocator_type Alloc{}; }; // NOLINTEND(readability-identifier-naming) diff --git a/include/phasar/Utils/Table.h b/include/phasar/Utils/Table.h index 6b5511f64..24ac50ac0 100644 --- a/include/phasar/Utils/Table.h +++ b/include/phasar/Utils/Table.h @@ -17,10 +17,14 @@ #ifndef PHASAR_UTILS_TABLE_H_ #define PHASAR_UTILS_TABLE_H_ +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/DefaultValue.h" + #include "llvm/Support/raw_ostream.h" #include #include +#include #include #include @@ -29,62 +33,54 @@ namespace psr { template class Table { -private: - std::unordered_map> Tab; - public: struct Cell { - Cell() = default; - Cell(R Row, C Col, const V Val) - : Row(Row), Column(Col), Val(std::move(Val)) {} - ~Cell() = default; - Cell(const Cell &) = default; - Cell &operator=(const Cell &) = default; - Cell(Cell &&) noexcept = default; - Cell &operator=(Cell &&) noexcept = default; - - [[nodiscard]] R getRowKey() const { return Row; } - [[nodiscard]] C getColumnKey() const { return Column; } - [[nodiscard]] V getValue() const { return Val; } + Cell() noexcept = default; + Cell(R Row, C Col, V Val) noexcept + : Row(std::move(Row)), Column(std::move(Col)), Value(std::move(Val)) {} + + [[nodiscard]] ByConstRef getRowKey() const noexcept { return Row; } + [[nodiscard]] ByConstRef getColumnKey() const noexcept { return Column; } + [[nodiscard]] ByConstRef getValue() const noexcept { return Value; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Cell &Cell) { return OS << "Cell: " << Cell.r << ", " << Cell.c << ", " << Cell.v; } - friend bool operator<(const Cell &Lhs, const Cell &Rhs) { - return std::tie(Lhs.Row, Lhs.Column, Lhs.Val) < - std::tie(Rhs.Row, Rhs.Column, Rhs.Val); + friend bool operator<(const Cell &Lhs, const Cell &Rhs) noexcept { + return std::tie(Lhs.Row, Lhs.Column, Lhs.Value) < + std::tie(Rhs.Row, Rhs.Column, Rhs.Value); } - friend bool operator==(const Cell &Lhs, const Cell &Rhs) { - return std::tie(Lhs.Row, Lhs.Column, Lhs.Val) == - std::tie(Rhs.Row, Rhs.Column, Rhs.Val); + friend bool operator==(const Cell &Lhs, const Cell &Rhs) noexcept { + return std::tie(Lhs.Row, Lhs.Column, Lhs.Value) == + std::tie(Rhs.Row, Rhs.Column, Rhs.Value); } - private: - R Row; - C Column; - V Val; + R Row{}; + C Column{}; + V Value{}; }; - Table() = default; - Table(const Table &T) = default; - Table &operator=(const Table &T) = default; + Table() noexcept = default; + + explicit Table(const Table &T) = default; + Table &operator=(const Table &T) = delete; + Table(Table &&T) noexcept = default; Table &operator=(Table &&T) noexcept = default; + ~Table() = default; void insert(R Row, C Column, V Val) { // Associates the specified value with the specified keys. - Tab[Row][Column] = std::move(Val); + Tab[std::move(Row)][std::move(Column)] = std::move(Val); } - void insert(const Table &T) { Tab.insert(T.table.begin(), T.table.end()); } - - void clear() { Tab.clear(); } + void clear() noexcept { Tab.clear(); } - [[nodiscard]] bool empty() const { return Tab.empty(); } + [[nodiscard]] bool empty() const noexcept { return Tab.empty(); } - [[nodiscard]] size_t size() const { return Tab.size(); } + [[nodiscard]] size_t size() const noexcept { return Tab.size(); } [[nodiscard]] std::set cellSet() const { // Returns a set of all row key / column key / value triplets. @@ -97,9 +93,25 @@ template class Table { return Result; } + template void foreachCell(Fn Handler) const { + for (const auto &M1 : Tab) { + for (const auto &M2 : M1.second) { + std::invoke(Handler, M1.first, M2.first, M2.second); + } + } + } + template void foreachCell(Fn Handler) { + for (auto &M1 : Tab) { + for (auto &M2 : M1.second) { + std::invoke(Handler, M1.first, M2.first, M2.second); + } + } + } + [[nodiscard]] std::vector cellVec() const { // Returns a vector of all row key / column key / value triplets. std::vector Result; + Result.reserve(Tab.size()); // better than nothing... for (const auto &M1 : Tab) { for (const auto &M2 : M1.second) { Result.emplace_back(M1.first, M2.first, M2.second); @@ -108,7 +120,7 @@ template class Table { return Result; } - [[nodiscard]] std::unordered_map column(C ColumnKey) const { + [[nodiscard]] std::unordered_map column(ByConstRef ColumnKey) const { // Returns a view of all mappings that have the given column key. std::unordered_map Column; for (const auto &Row : Tab) { @@ -119,31 +131,8 @@ template class Table { return Column; } - [[nodiscard]] std::multiset columnKeySet() const { - // Returns a set of column keys that have one or more values in the table. - std::multiset Result; - for (const auto &M1 : Tab) { - for (const auto &M2 : M1.second) { - Result.insert(M2.first); - } - } - return Result; - } - - [[nodiscard]] std::unordered_map> - columnMap() const { - // Returns a view that associates each column key with the corresponding map - // from row keys to values. - std::unordered_map> Result; - for (const auto &M1 : Tab) { - for (const auto &M2 : Tab.second) { - Result[M2.first][M1.first] = M2.second; - } - } - return Result; - } - - [[nodiscard]] bool contains(R RowKey, C ColumnKey) const { + [[nodiscard]] bool contains(ByConstRef RowKey, + ByConstRef ColumnKey) const noexcept { // Returns true if the table contains a mapping with the specified row and // column keys. if (auto RowIter = Tab.find(RowKey); RowIter != Tab.end()) { @@ -152,7 +141,7 @@ template class Table { return false; } - [[nodiscard]] bool containsColumn(C ColumnKey) const { + [[nodiscard]] bool containsColumn(ByConstRef ColumnKey) const noexcept { // Returns true if the table contains a mapping with the specified column. for (const auto &M1 : Tab) { if (M1.second.count(ColumnKey)) { @@ -162,80 +151,92 @@ template class Table { return false; } - [[nodiscard]] bool containsRow(R RowKey) const { + [[nodiscard]] bool containsRow(ByConstRef RowKey) const noexcept { // Returns true if the table contains a mapping with the specified row key. return Tab.count(RowKey); } - [[nodiscard]] bool containsValue(const V &Value) const { - // Returns true if the table contains a mapping with the specified value. - for (const auto &M1 : Tab) { - for (const auto &M2 : M1.second) { - if (Value == M2.second) { - return true; - } - } - } - return false; + [[nodiscard]] V &get(R RowKey, C ColumnKey) { + // Returns the value corresponding to the given row and column keys, or V() + // if no such mapping exists. + return Tab[std::move(RowKey)][std::move(ColumnKey)]; } - [[nodiscard]] V &get(R RowKey, C ColumnKey) { - // Returns the value corresponding to the given row and column keys, or null + [[nodiscard]] ByConstRef get(ByConstRef RowKey, + ByConstRef ColumnKey) const noexcept { + // Returns the value corresponding to the given row and column keys, or V() // if no such mapping exists. - return Tab[RowKey][ColumnKey]; + auto OuterIt = Tab.find(RowKey); + if (OuterIt == Tab.end()) { + return getDefaultValue(); + } + + auto It = OuterIt->second.find(ColumnKey); + if (It == OuterIt->second.end()) { + return getDefaultValue(); + } + + return It->second; } - V remove(R RowKey, C ColumnKey) { + V remove(ByConstRef RowKey, ByConstRef ColumnKey) { // Removes the mapping, if any, associated with the given keys. - V Val = Tab[RowKey][ColumnKey]; - Tab[RowKey].erase(ColumnKey); - return Val; + + auto OuterIt = Tab.find(RowKey); + if (OuterIt == Tab.end()) { + return V(); + } + + auto It = OuterIt->second.find(ColumnKey); + if (It == OuterIt->second.end()) { + return V(); + } + + auto Ret = std::move(It->second); + + OuterIt->second.erase(It); + if (OuterIt->second.empty()) { + Tab.erase(OuterIt); + } + + return Ret; } - void remove(R RowKey) { Tab.erase(RowKey); } + void remove(ByConstRef RowKey) { Tab.erase(RowKey); } [[nodiscard]] std::unordered_map &row(R RowKey) { // Returns a view of all mappings that have the given row key. return Tab[RowKey]; } - [[nodiscard]] std::multiset rowKeySet() const { - // Returns a set of row keys that have one or more values in the table. - std::multiset Result; - for (const auto &M1 : Tab) { - Result.insert(M1.first); + [[nodiscard]] ByConstRef> + row(ByConstRef RowKey) const noexcept { + // Returns a view of all mappings that have the given row key. + auto It = Tab.find(RowKey); + if (It == Tab.end()) { + return getDefaultValue>(); } - return Result; + return It->second; } - [[nodiscard]] std::unordered_map> rowMap() const { + [[nodiscard]] const std::unordered_map> & + rowMap() const noexcept { // Returns a view that associates each row key with the corresponding map // from column keys to values. return Tab; } - [[nodiscard]] std::multiset values() const { - // Returns a collection of all values, which may contain duplicates. - std::multiset Result; - for (const auto &M1 : Tab) { - for (const auto &M2 : M1.second) { - Result.insert(M2.second); - } - } - return Result; + bool operator==(const Table &Other) noexcept { + return Tab == Other.Tab; } - friend bool operator==(const Table &Lhs, const Table &Rhs) { - return Lhs.table == Rhs.table; - } - - friend bool operator<(const Table &Lhs, const Table &Rhs) { - return Lhs.table < Rhs.table; + bool operator<(const Table &Other) noexcept { + return Tab < Other.Tab; } friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Table &Tab) { - for (const auto &M1 : Tab.table) { + for (const auto &M1 : Tab.Tab) { for (const auto &M2 : M1.second) { OS << "< " << M1.first << " , " << M2.first << " , " << M2.second << " >\n"; @@ -243,6 +244,9 @@ template class Table { } return OS; } + +private: + std::unordered_map> Tab{}; }; } // namespace psr diff --git a/include/phasar/Utils/Timer.h b/include/phasar/Utils/Timer.h new file mode 100644 index 000000000..f485aa0cb --- /dev/null +++ b/include/phasar/Utils/Timer.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2023 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_TIMER_H +#define PHASAR_UTILS_TIMER_H + +#include "llvm/ADT/FunctionExtras.h" + +#include + +namespace psr { +class Timer { +public: + Timer(llvm::unique_function + WithElapsed) noexcept + : WithElapsed(std::move(WithElapsed)), + Start(std::chrono::steady_clock::now()) {} + + Timer(Timer &&) noexcept = default; + Timer &operator=(Timer &&) noexcept = default; + Timer(const Timer &) = delete; + Timer &operator=(const Timer &) = delete; + + ~Timer() { + if (WithElapsed) { + auto End = std::chrono::steady_clock::now(); + WithElapsed(End - Start); + } + } + +private: + llvm::unique_function WithElapsed; + std::chrono::steady_clock::time_point Start; +}; +} // namespace psr + +#endif // PHASAR_UTILS_TIMER_H diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 0957292bb..f4c6736f2 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -10,8 +10,10 @@ #ifndef PHASAR_UTILS_TYPETRAITS_H #define PHASAR_UTILS_TYPETRAITS_H +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -19,25 +21,32 @@ #include namespace psr { + +#if __cplusplus < 202002L +template struct type_identity { using type = T; }; +#else +template using type_identity = std::type_identity; +#endif + // NOLINTBEGIN(readability-identifier-naming) namespace detail { template struct is_iterable : std::false_type {}; // NOLINT template -struct is_iterable().begin()), - decltype(std::declval().end())>> : std::true_type { -}; +struct is_iterable())), + decltype(llvm::adl_end(std::declval()))>> + : public std::true_type {}; + template struct is_iterable_over : std::false_type {}; // NOLINT template struct is_iterable_over< T, U, - std::enable_if_t< - is_iterable::value && - std::is_convertible_v().begin()), U>>> - : std::true_type {}; + std::enable_if_t::value && + std::is_same_v()))>>>> + : public std::true_type {}; template struct is_pair : std::false_type {}; // NOLINT template @@ -66,6 +75,25 @@ template struct has_str().str())> : std::true_type { }; // NOLINT +template struct has_reserve : std::false_type {}; +template +struct has_reserve< + T, std::void_t().reserve(size_t(0)))>> + : std::true_type {}; + +template struct has_adl_to_string { + template ())))> + static std::true_type test(int); + template ()))> + static std::true_type test(long); + template static std::false_type test(...); + + static constexpr bool value = + std::is_same_v; +}; + template struct has_erase_iterator : std::false_type {}; // NOLINT template @@ -84,6 +112,10 @@ struct is_llvm_hashable : std::false_type {}; // NOLINT template struct is_llvm_hashable()))> // NOLINT : std::true_type {}; +template +struct is_llvm_hashable()))> // NOLINT + : std::true_type {}; template