diff --git a/.clang-tidy b/.clang-tidy index 54f04105c7..94ed69701b 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -17,6 +17,8 @@ Checks: '-*, -readability-convert-member-functions-to-static, -readability-isolate-declaration, -readability-identifier-length, + -readability-redundant-member-init, + -readability-use-anyofallof, cppcoreguidelines-*, -cppcoreguidelines-avoid-non-const-global-variables, -cppcoreguidelines-pro-bounds-array-to-pointer-decay, @@ -57,7 +59,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|DToString|NToString|FToString|LToString) + value: (try_emplace|from_json|to_json|equal_to|to_string|DToString|NToString|FToString|LToString|hash_value) - key: cppcoreguidelines-special-member-functions.AllowSoleDefaultDtor value: 1 - key: cppcoreguidelines-special-member-functions.AllowMissingMoveFunctions diff --git a/.dockerignore b/.dockerignore index ea7ac67581..2b31c69a2b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,2 +1,16 @@ -./docker -./build +* +!.git +!cmake/ +!config/ +!include/ +!lib/ +!test/ +!tools/ +!unittests/ +!utils/*.sh +!utils/*.py + +!.gitmodules +!CMakeLists.txt +!Config.cmake.in +!config.h.in diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3e74129213..8eb7395094 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -32,3 +32,6 @@ Dockerfile @janniclas /include/phasar/Utils/OnTheFlyAnalysisPrinter.h @sritejakv /include/phasar/PhasarLLVM/Utils/SourceMgrPrinter.h @sritejakv /lib/PhasarLLVM/Utils/SourceMgrPrinter.cpp @sritejakv + +/conanfile.py @jusito +/utils/conan/ @jusito diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8dab4ed41..30b1cd00c2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,19 +6,32 @@ on: pull_request: branches: [ master, development ] +# TODO test in tree build? +# TODO test conan build jobs: build: - runs-on: ubuntu-20.04 + runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: - compiler: [ [clang++-14, clang-14] ] - build: [ Debug, Release ] + os: [ubuntu-20.04, ubuntu-24.04-arm] + compiler: [ [clang++-19, clang-19, "clang-19 libclang-rt-19-dev"] ] + build: [ Debug, Release, DebugLibdeps ] include: - build: Debug - flags: -DPHASAR_BUILD_DYNLIB=ON -DPHASAR_ENABLE_SANITIZERS=ON + cmake_build_type: Debug + flags: -DPHASAR_ENABLE_SANITIZERS=ON - build: Release - flags: -DPHASAR_ENABLE_DYNAMIC_LOG=OFF -DPHASAR_DEBUG_LIBDEPS=ON -DBUILD_SHARED_LIBS=ON + cmake_build_type: Release + flags: -DPHASAR_ENABLE_DYNAMIC_LOG=OFF -DPHASAR_BUILD_DYNLIB=ON -DPHASAR_ENABLE_SANITIZERS=ON + - build: DebugLibdeps + cmake_build_type: Debug + flags: -DPHASAR_DEBUG_LIBDEPS=ON -DBUILD_SHARED_LIBS=ON + exclude: + - os: ubuntu-24.04-arm + build: Debug + - os: ubuntu-24.04-arm + build: DebugLibdeps continue-on-error: false steps: @@ -31,43 +44,42 @@ jobs: - name: Install Phasar Dependencies shell: bash run: | - ./utils/InstallAptDependencies.sh - sudo apt-get -y install --no-install-recommends libboost-graph-dev + ./utils/InstallAptDependencies.sh --noninteractive tzdata ${{ matrix.compiler[2] }} - - name: Install Strategy Dependencies + - uses: swift-actions/setup-swift@v2 + if: matrix.os == 'ubuntu-20.04' + with: + swift-version: "5.8.1" + - name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }} including swift + if: matrix.os == 'ubuntu-20.04' + env: + CXX: ${{ matrix.compiler[0] }} + CC: ${{ matrix.compiler[1] }} shell: bash run: | - sudo apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key - sudo add-apt-repository -y 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main' - sudo apt-get update - sudo apt-get -y install --no-install-recommends \ - ${{ matrix.compiler[1] }} \ - llvm-14-dev \ - libllvm14 \ - libclang-common-14-dev \ - libclang-14-dev \ - libclang-cpp14-dev \ - clang-tidy-14 \ - libclang-rt-14-dev + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ + -DBUILD_PHASAR_CLANG=OFF \ + -DBUILD_SWIFT_TESTS=ON \ + -DPHASAR_USE_Z3=ON \ + ${{ matrix.flags }} \ + -G Ninja + ninja -C build - - uses: swift-actions/setup-swift@v1 - with: - swift-version: "5.8.1" - name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }} + if: matrix.os != 'ubuntu-20.04' env: CXX: ${{ matrix.compiler[0] }} CC: ${{ matrix.compiler[1] }} shell: bash run: | - mkdir build - cd build - cmake .. \ - -DCMAKE_BUILD_TYPE=${{ matrix.build }} \ - -DBUILD_SWIFT_TESTS=ON \ + cmake -S . -B build \ + -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \ + -DBUILD_PHASAR_CLANG=OFF \ -DPHASAR_USE_Z3=ON \ ${{ matrix.flags }} \ -G Ninja - cmake --build . + ninja -C build - name: Run Unittests shell: bash diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 27ea46af6a..702184a4f4 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -2,49 +2,40 @@ name: Docker Image on: push: - branches: [ development ] + branches: [ master, development ] + pull_request: + branches: [ master, development ] jobs: push_to_registries: name: Push Docker image to multiple registries - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-24.04, ubuntu-24.04-arm] permissions: packages: write contents: read steps: - name: Check out the repo - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 + uses: actions/checkout@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - # If we want to publish an image of PhASAR to the official docker hub we - # can just use the following code and set the corresponding secrets. - # - name: Log in to Docker Hub - # uses: docker/login-action@f054a8b539a109f9f41c372932f1ae047eff08c9 - # with: - # username: ${{ secrets.DOCKER_USERNAME }} - # password: ${{ secrets.DOCKER_TOKEN }} - + uses: docker/setup-buildx-action@v3 - name: Log in to the Container registry - uses: docker/login-action@v2 + uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Extract metadata (tags, labels) for Docker id: meta uses: docker/metadata-action@v4 with: images: ghcr.io/${{ github.repository }} - # here we could add a second image for the official docker registry - # sse/phasar - name: Build and push Docker images - uses: docker/build-push-action@v3 + uses: docker/build-push-action@v6 with: context: . - platforms: linux/amd64,linux/arm64 - push: true + push: ${{ github.ref == 'refs/heads/development' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} diff --git a/.gitignore b/.gitignore index 1cc71e4737..f86934e8b1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,9 +4,11 @@ bin/* # build directories for cmake -build/ -build_*/ -build-*/ +build*/ +cmake-build*/ +cmake-install*/ +CMakeUserPresets.json +venv/ # LLVM project llvm-project/* @@ -26,6 +28,7 @@ doc/* # log/ directory log/* +**/*/logs/ # CMake build dir build/* @@ -43,12 +46,15 @@ build/* .idea/* # cache -.cache/* +.cache/ ##### ignored files # ignore all auto-generated LLVM IR code *.ll +!utils/conan/llvm-core/all/test_package/test_function.ll +!test_package/example.ll +!test_package_cmake/example.ll # auto generated test files *.test diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fb9bed9847..786e0aba36 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,9 @@ repos: rev: v4.3.0 hooks: - id: trailing-whitespace + exclude: "^.*\\.patch$" - id: end-of-file-fixer + exclude: "^.*\\.patch$" - id: check-yaml - id: check-added-large-files - id: requirements-txt-fixer diff --git a/BreakingChanges.md b/BreakingChanges.md index 027768f00d..8c17daa8ae 100644 --- a/BreakingChanges.md +++ b/BreakingChanges.md @@ -1,5 +1,25 @@ # Breaking Changes +## development HEAD + +*None* + +## v2503 + +- The `DTAResolver` and the cli option `--call-graph-analysis=dta` do not work anymore (due to opaque pointers) and will be removed for the next release. Please use the `OTF` or `RTA` resolver instead. +- The default type-hierarchy implementation has been changed from `LLVMTypeHierarchy` to `DIBasedTypeHierarchy`. This also requires all affected analyses to be performed on LLVM IR that contains debug information. +- Removed the phasar-library `phasar_controller`. It is now part of the tool `phasar-cli`. +- The API of the `TypeHierarchy` interface (and thus the `LLVMTypeHierarchy` and `DIBasedTypeHierarchy` as well) has changed: + - No handling of the super-type relation (only sub-types) + - No VTable handling anymore -- has been out-sourced into `LLVMVFTableProvider` + - minor API changes +- The constructors of the call-graph resolvers have changed. They: + - take the `LLVMProjectIRDB` as pointer-to-const + - take an additional second parameter of type `const LLVMVFTableProvider *` + - not necessarily require a `LLVMTypeHierarchy` anymore +- Some constructors of `LLVMBasedICFG` do not accept a `LLVMTypeHierarchy` pointer anymore +- Removed IfdsFieldSensTaintAnalysis as it relies on LLVM's deprecated typed-pointers. + ## v2403 - Versioning scheme has been changed from `` to `` diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ddf030caf..2b8d993810 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.14) +cmake_minimum_required (VERSION 3.14...3.28) # Avoid IPO/LTO Warnings: cmake_policy(SET CMP0069 NEW) @@ -8,6 +8,12 @@ set(CMAKE_POLICY_DEFAULT_CMP0069 NEW) cmake_policy(SET CMP0077 NEW) set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) +if ("${CMAKE_VERSION}" GREATER_EQUAL "3.21") + # Allow overwriting cache variables of external projects from this CMakeLists file + cmake_policy(SET CMP0126 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) +endif() + # Allow portable use of CMAKE_VISIBILITY_INLINES_HIDDEN not only for shared libraries cmake_policy(SET CMP0063 NEW) set(CMAKE_POLICY_DEFAULT_CMP0063 NEW) @@ -18,19 +24,27 @@ if (DEFINED LLVM_MAIN_SRC_DIR) endif() if (NOT PHASAR_IN_TREE) - project (phasar) - set(CMAKE_PROJECT_NAME "phasar") + project (phasar + LANGUAGES C CXX + DESCRIPTION "A LLVM-based static analysis framework." + ) endif () +set(PHASAR_VERSION 2503) -option(PHASAR_EXPERIMENTAL_CXX20 "Build phasar in C++20 mode. This is an experimental feature" OFF) +# NOTE: When we require cmake >= 3.21, we can use PROJECT_IS_TOP_LEVEL instead +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) + set(PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT ON) +else() + set(PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT OFF) +endif() -set(CMAKE_EXPORT_COMPILE_COMMANDS YES) +option(PHASAR_EXPERIMENTAL_CXX20 "Build phasar in C++20 mode. This is an experimental feature" OFF) if(PHASAR_EXPERIMENTAL_CXX20) - message(STATUS "Selected experimental C++20 build") + message(DEPRECATION "The option PHASAR_EXPERIMENTAL_CXX20 is deprecated and will be removed in a future version of PhASAR. Use CMAKE_CXX_STANDARD=20 instead.") set(CMAKE_CXX_STANDARD 20) -else() - set(CMAKE_CXX_STANDARD 17) endif() + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) @@ -45,7 +59,7 @@ 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) +if (NOT CMAKE_BUILD_TYPE AND NOT GENERATOR_IS_MULTI_CONFIG AND NOT PHASAR_IN_TREE) 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 () @@ -64,7 +78,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE) # https://reviews.llvm.org/D157613 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_DEBUG " -fno-omit-frame-pointer") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer") string(APPEND CMAKE_CXX_FLAGS_RELEASE "") @@ -131,40 +145,45 @@ if (PHASAR_ENABLE_SANITIZERS) 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}") +option(PHASAR_ALLOW_LTO_IN_RELEASE_BUILD "Use link-time-optimization (LTO) in Release builds, if possible (default is ON)" ON) +if(PHASAR_ALLOW_LTO_IN_RELEASE_BUILD) + 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() endif() -# Enable testing -enable_testing() - -option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ON) +option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT}) 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_BUILD_IR "Build IR test code (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT}) 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_TOOLS "Build PhASAR-based tools (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT}) +option(PHASAR_USE_CONAN "Using Conan for dependencies instead of submodules." OFF) + +#option(BUILD_SHARED_LIBS "Build shared libraries (default is ON)" ON) option(PHASAR_BUILD_DYNLIB "Build one fat shared library. Requires BUILD_SHARED_LIBS to be turned OFF (default is OFF)" OFF) if(PHASAR_BUILD_DYNLIB AND BUILD_SHARED_LIBS) message(FATAL_ERROR "PHASAR_BUILD_DYNLIB is incompatible with BUILD_SHARED_LIBS") endif() -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) @@ -198,7 +217,7 @@ else() endif() # Logger -option(PHASAR_ENABLE_DYNAMIC_LOG "Makes it possible to switch the logger on and off at runtime (default is ON)" ON) +option(PHASAR_ENABLE_DYNAMIC_LOG "Makes it possible to switch the logger on and off at runtime; otherwise, it is turned off at compile-time (default is ON)" ON) if (PHASAR_ENABLE_DYNAMIC_LOG) message(STATUS "Dynamic log enabled") @@ -207,8 +226,9 @@ else() message(STATUS "Dynamic log disabled") endif() -# RPATH -if (NOT PHASAR_IN_TREE) +if (PHASAR_USE_CONAN) +elseif (NOT PHASAR_IN_TREE) + # RPATH set(CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}) if (NOT "${CMAKE_INSTALL_LIBDIR}" STREQUAL "lib") @@ -217,8 +237,16 @@ if (NOT PHASAR_IN_TREE) endif() set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) + + # Export set + set(PHASAR_DEPS_EXPORT_SET PhasarDepsExports) +else() + # Export set + set(PHASAR_DEPS_EXPORT_SET LLVMExports) endif() +set(PHASAR_DEPS_INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/phasar/deps) + # Filesystem if (LLVM_ENABLE_LIBCXX) set(PHASAR_STD_FILESYSTEM c++fs) @@ -227,189 +255,96 @@ else() 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)") +set(PHASAR_CUSTOM_CONFIG_INSTALL_DIR "" CACHE STRING "If set, customizes the directory, where configuration files for PhASAR are installed (default is ${CMAKE_INSTALL_PREFIX}/.phasar-config)") if ("${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}" STREQUAL "") - set(PHASAR_CONFIG_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/.phasar-config/") + set(PHASAR_CONFIG_INSTALL_DIR ".phasar-config/") else() set(PHASAR_CONFIG_INSTALL_DIR "${PHASAR_CUSTOM_CONFIG_INSTALL_DIR}") endif() - -# 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 REQUIRED) +find_package(Boost 1.65.1 COMPONENTS graph REQUIRED CONFIG) # 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 -# in the installed state the nlohmann_json_schema_validator needs the nlohmann_json package which needs -# to be installed. -# 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 -set(BUILD_TESTS OFF CACHE BOOL "Build json-schema-validator-tests") - -if (PHASAR_IN_TREE) - set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS nlohmann_json_schema_validator) - - set (PHASAR_USE_Z3 OFF) -endif() -# Json Schema Validator -set(JSON_VALIDATOR_INSTALL ON) -add_subdirectory(external/json-schema-validator) +include(add_nlohmann_json) +add_nlohmann_json() +add_json_schema_validator() # Googletest if (NOT PHASAR_IN_TREE) - set(BUILD_GMOCK OFF) - set(INSTALL_GTEST OFF) - add_subdirectory(external/googletest EXCLUDE_FROM_ALL) - set(GTEST_INCLUDE_DIR "external/googletest/googletest/include") + if(PHASAR_BUILD_UNITTESTS AND NOT TARGET gtest) + include(FetchContent) + + set(INSTALL_GTEST OFF) + FetchContent_Declare( + googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.16.0 + ) + FetchContent_MakeAvailable(googletest) + endif() else() # Set llvm distributed includes for gtest header 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) +find_package(SQLite3) +if(SQLite3_FOUND) + set(PHASAR_HAS_SQLITE ON) +else() + set(PHASAR_HAS_SQLITE OFF) +endif() 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) - find_library(LLVM_LIBRARY NAMES LLVM PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) - - 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") +if (NOT PHASAR_LLVM_VERSION) + set(PHASAR_LLVM_VERSION 15) endif() +include(add_llvm) +add_llvm() # 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}) +if(PHASAR_IN_TREE) + set (PHASAR_USE_Z3 OFF) +else() + 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 + + # FindZ3.cmake by llvm tries to compile a snippet with Z3 which crashes on arm with sanitizers enabled + set(SAFE_CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "") + find_package(Z3 4.7.1 REQUIRED) + set(CMAKE_CXX_FLAGS "${SAFE_CMAKE_CXX_FLAGS}") + + if(Z3_FOUND) + 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() endif() -endif(PHASAR_USE_Z3) +endif() # Clang 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() - - 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) + add_clang() +endif() # Set up clang-tidy to run during PhASAR's compilation to indicate code smells if (PHASAR_ENABLE_CLANG_TIDY_DURING_BUILD) @@ -427,11 +362,8 @@ if(NOT PHASAR_IN_TREE) add_definitions(${LLVM_DEFINITIONS_LIST}) endif() -# Installed config -configure_file(config.h.in include/phasar/Config/phasar-config.h @ONLY) - # Warnings -option(PHASAR_ENABLE_WARNINGS "Enable warnings" ON) +option(PHASAR_ENABLE_WARNINGS "Enable warnings" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT}) if (PHASAR_ENABLE_WARNINGS) if (MSVC) string(APPEND CMAKE_CXX_FLAGS " /W4") @@ -440,6 +372,18 @@ if (PHASAR_ENABLE_WARNINGS) endif() endif (PHASAR_ENABLE_WARNINGS) + +# Headers +add_library(phasar_interface INTERFACE) +file(GLOB_RECURSE PHASAR_PUBLIC_HEADERS include/*.h include/*.def) +configure_file(config.h.in include/phasar/Config/phasar-config.h @ONLY) + +target_sources(phasar_interface INTERFACE + FILE_SET HEADERS + BASE_DIRS "${PHASAR_SRC_DIR}/include" "${PHASAR_BINARY_DIR}/include" + FILES ${PHASAR_PUBLIC_HEADERS} "${PHASAR_BINARY_DIR}/include/phasar/Config/phasar-config.h" +) + # 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}") @@ -448,7 +392,9 @@ add_cxx_compile_definitions(PHASAR_BUILD_DIR="${CMAKE_BINARY_DIR}") add_subdirectory(lib) # phasar-based binaries -add_subdirectory(tools) +if(PHASAR_BUILD_TOOLS) + add_subdirectory(tools) +endif() # Swift tests option(BUILD_SWIFT_TESTS "Builds the Swift tests (Swift compiler has to be installed manually beforehand!)" OFF) @@ -461,6 +407,8 @@ endif(BUILD_SWIFT_TESTS) # Add Phasar unittests and build all IR test code if (PHASAR_BUILD_UNITTESTS) message("Phasar unittests") + + enable_testing() add_subdirectory(unittests) if(NOT PHASAR_BUILD_IR) message(WARNING "Set PHASAR_BUILD_IR=ON, because PHASAR_BUILD_UNITTESTS is ON") @@ -480,51 +428,38 @@ set(LIBRARY_INSTALL_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "Install dir of lib # Install targets of phasar-cli, other executables, and libraries are to be # found in the individual subdirectories of tools/ -# Install Phasar include directory -install(DIRECTORY include/ - DESTINATION include - FILES_MATCHING - PATTERN "*.def" - PATTERN "*.h" -) - -# 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 + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) - - # Install the export-set containing all the phasar targets - install(EXPORT PhasarExports - FILE PhasarExports.cmake - NAMESPACE phasar:: - DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar" - ) + if (NOT PHASAR_USE_CONAN) + # Install the export-set containing all the phasar targets + install(EXPORT PhasarExports + FILE PhasarExports.cmake + NAMESPACE phasar:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar" + ) + install(EXPORT ${PHASAR_DEPS_EXPORT_SET} + FILE ${PHASAR_DEPS_EXPORT_SET}.cmake + NAMESPACE phasar::deps:: + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar" + ) + endif() else() install(TARGETS phasar_interface EXPORT LLVMExports + FILE_SET HEADERS DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ) 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 Phasar utils helper scripts install(DIRECTORY utils/ DESTINATION bin FILES_MATCHING PATTERN "CodeGen" EXCLUDE # CodeGen does not contain files to install + PATTERN "conan" EXCLUDE # Don't install conan utils PATTERN "phasar-*" PERMISSIONS OWNER_EXECUTE OWNER_WRITE OWNER_READ GROUP_EXECUTE GROUP_READ @@ -550,7 +485,7 @@ configure_package_config_file( write_basic_package_version_file( ${CMAKE_CURRENT_BINARY_DIR}/phasarConfigVersion.cmake - VERSION 2403 + VERSION ${PHASAR_VERSION} COMPATIBILITY SameMajorVersion ) diff --git a/Config.cmake.in b/Config.cmake.in index 305cf04e28..e531879c5d 100644 --- a/Config.cmake.in +++ b/Config.cmake.in @@ -1,22 +1,27 @@ -set(PHASAR_VERSION 2403) +set(PHASAR_VERSION @PHASAR_VERSION@) @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 graph REQUIRED) -find_package(LLVM 14 REQUIRED CONFIG) +set(PHASAR_LLVM_VERSION @PHASAR_LLVM_VERSION@) + +include("${CMAKE_CURRENT_LIST_DIR}/PhasarDepsExports.cmake") +find_dependency(Boost 1.65.1 COMPONENTS graph REQUIRED CONFIG) +find_dependency(LLVM ${PHASAR_LLVM_VERSION} 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@) +set(PHASAR_HAS_SQLITE @PHASAR_HAS_SQLITE@) if (PHASAR_USE_Z3) - find_dependency(Z3) + find_dependency(Z3 REQUIRED) +endif() +if(PHASAR_HAS_SQLITE) + find_dependency(SQLite3 REQUIRED) endif() set(PHASAR_COMPONENTS @@ -38,7 +43,6 @@ set(PHASAR_COMPONENTS llvm llvm_ifdside analysis_strategy - controller ) list(REMOVE_DUPLICATES phasar_FIND_COMPONENTS) @@ -69,6 +73,8 @@ if (NOT DEFINED phasar_FOUND OR phasar_FOUND EQUAL TRUE) endif() function(phasar_config executable) + message(DEPRECATION "The function 'phasar_config' is deprecated. Use target_link_libraries(${executable} PUBLIC phasar::phasar) instead!") + target_link_libraries(${executable} PUBLIC ${PHASAR_NEEDED_LIBS} diff --git a/Dockerfile b/Dockerfile index 2bed11717b..11d69a1894 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,61 +1,32 @@ -FROM ubuntu:22.04 -ARG LLVM_INSTALL_DIR="/usr/local/llvm-14" -LABEL Name=phasar Version=2403 - -RUN apt -y update && apt install bash sudo -y - - -WORKDIR /usr/src/phasar -RUN mkdir -p /usr/src/phasar/utils - -COPY ./utils/InitializeEnvironment.sh /usr/src/phasar/utils/ -RUN ./utils/InitializeEnvironment.sh - -RUN apt-get -y install --no-install-recommends \ - cmake \ - ninja-build \ - libstdc++6 \ - libboost-graph-dev - -COPY ./utils/InstallAptDependencies.sh /usr/src/phasar/utils/ -RUN ./utils/InstallAptDependencies.sh - -RUN apt-get update && \ - apt-get install -y software-properties-common - -RUN apt-key adv --fetch-keys https://apt.llvm.org/llvm-snapshot.gpg.key && \ - add-apt-repository -y 'deb http://apt.llvm.org/focal/ llvm-toolchain-focal-14 main' && \ - apt-get update && \ - apt-get -y install --no-install-recommends \ - clang-14 \ - llvm-14-dev \ - libllvm14 \ - libclang-common-14-dev \ - libclang-14-dev \ - libclang-cpp14-dev \ - clang-tidy-14 \ - libclang-rt-14-dev - -RUN pip3 install Pygments pyyaml - - - -# installing wllvm -RUN pip3 install wllvm - -ENV CC=/usr/bin/clang-14 -ENV CXX=/usr/bin/clang++-14 - -COPY . /usr/src/phasar - -RUN git submodule init -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 . - -ENTRYPOINT [ "./build/tools/phasar-cli/phasar-cli" ] +ARG baseimage="ubuntu:24.04" +FROM "$baseimage" as build + +RUN --mount=type=bind,source=./utils/InstallAptDependencies.sh,target=/InstallAptDependencies.sh \ + set -eux; \ + ./InstallAptDependencies.sh --noninteractive tzdata clang-19 libclang-rt-19-dev + +ENV CC=/usr/bin/clang-19 \ + CXX=/usr/bin/clang++-19 + +FROM build + +ARG RUN_TESTS=OFF +RUN --mount=type=bind,source=.,target=/usr/src/phasar,rw \ + set -eux; \ + cd /usr/src/phasar; \ + git submodule update --init; \ + cmake -S . -B cmake-build/Release \ + -DCMAKE_BUILD_TYPE=Release \ + -DPHASAR_TARGET_ARCH="" \ + -DPHASAR_ENABLE_SANITIZERS=ON \ + -DBUILD_PHASAR_CLANG=ON \ + -DPHASAR_USE_Z3=ON \ + -DPHASAR_BUILD_UNITTESTS=$RUN_TESTS \ + -DPHASAR_BUILD_IR=$RUN_TESTS \ + -DPHASAR_BUILD_OPENSSL_TS_UNITTESTS=OFF \ + -G Ninja; \ + ninja -C cmake-build/Release install; \ + [ "${RUN_TESTS}" = "ON" ] && ctest --test-dir cmake-build/Release --output-on-failure || true; \ + phasar-cli --version + +ENTRYPOINT [ "phasar-cli" ] diff --git a/README.md b/README.md index 44ea59d4d7..d15020fae3 100644 --- a/README.md +++ b/README.md @@ -5,26 +5,31 @@ [![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 2403 +Version 2503 ## Secure Software Engineering Group PhASAR is primarily developed and maintained by the Secure Software Engineering Group at Heinz Nixdorf Institute (University of Paderborn) and Fraunhofer IEM. -Lead developers of PhASAR are: Fabian Schiebel (@fabianbs96)(), Martin Mory (@MMory)(), Philipp Dominik Schubert (@pdschubert)() and others. +PhASAR was initially developed by Philipp Dominik Schubert (@pdschubert)(). - +Currently, PhASAR is maintained by +- Fabian Schiebel (@fabianbs96)() +- Sriteja Kummita (@sritejakv) +- Lucas Briese (@jusito) +- Martin Mory (@MMory)() +- *others* -## Required version of the C++ standard +## Required Version of the C++ Standard -PhASAR requires C++-17. +PhASAR requires at least C++-17. -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. +However, building in C++20 mode is supported. You may enable this setting the cmake variable `CMAKE_CXX_STANDARD` to `20`. +Although phasar currently does not make use of C++-20 features (except for some `concept`s behind an #ifdef border), your client application that just *uses* phasar as a library may want to use C++20 ealier. -## Currently supported version of LLVM +## Currently Supported Version of LLVM -PhASAR is currently set up to support LLVM-14.0.* +PhASAR is currently set up to support LLVM-15.0.* ## What is PhASAR? @@ -68,41 +73,7 @@ The bootstrap script may ask for superuser permissions (to install the dependenc For subsequent builds, see [Compiling PhASAR](#compiling-phasar-if-not-already-done-using-the-installation-scripts). -## 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 - -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 -``` - -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) +### Compiling PhASAR (if not already done using the bootstrap script) Set the system's variables for the C and C++ compiler to clang: @@ -141,12 +112,6 @@ After compilation using cmake the following two binaries can be found in the bui + `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: - -`$ ./phasar-cli --help` - -in order to display the manual and help message. - Please be careful and check if errors occur during the compilation. When using CMake to compile PhASAR the following optional parameters can be used: @@ -166,39 +131,69 @@ When using CMake to compile PhASAR the following optional parameters can be used | **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)| +| **CMAKE_CXX_STANDARD** : INT|Build phasar in C++17 or C++20 mode (default is 17)| You can use these parameters either directly or modify the installer-script `bootstrap.sh` -#### A remark on compile time +#### A Remark on Compile Time C++'s long compile times are always a pain. As shown in the above, when using cmake the compilation can easily be run in parallel, resulting in shorter compilation times. Make use of it! -### Running a test solver +### Running a Test Solver To test if everything works as expected please run the following command: `$ phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest` +You can find the `phasar-cli` tool in the build-tree under `tools/phasar-cli`. + If you obtain output other than a segmentation fault or an exception terminating the program abnormally everything works as expected. +### Building PhASAR on a MacOS System + +Due to unfortunate updates to MacOS and the handling of C++, especially on the newer M1 processors, we can't support native development on Mac. +The easiest solution to develop PhASAR on a Mac right now is to use [dockers development environments](https://docs.docker.com/desktop/dev-environments/). Clone this repository as described in their documentation. Afterwards, you have to login once manually, as a root user by running `docker exec -it -u root /bin/bash` to complete the rest of the build process as described in this readme (install submodules, run bootstrap.sh, ...). +Now you can just attach your docker container to VS Code or any other IDE, which supports remote development. + +## Installation + +PhASAR can be installed using the installer scripts as explained in the following. +However, you do not need to install PhASAR in order to use it. + +### Installing PhASAR on an Ubuntu System + +In the following, we would like to give an complete example of how to install +PhASAR using an Ubuntu or Unix-like system. + +Therefore, we provide an installation script. To install PhASAR, just navigate to the top-level +directory of PhASAR and use the following command: + +```bash +./bootstrap.sh --install +``` + +The bootstrap script may ask for superuser permissions. + +Done! + +If You have already built phasar, you can just invoke +```bash +sudo ninja install +``` + ## How to use PhASAR? -We recomment using phasar as a library with `cmake`. +We recomment using phasar as a library with `cmake` or `conan`. 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`. +In this case, just `add_subdirectory` the phasar submodule directory 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 +add_subdirectory(external/phasar EXCLUDE_FROM_ALL) # Build phasar with your tool ... @@ -212,6 +207,28 @@ 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). +## How to use with Conan v2 ? + +To export the recipe and dependencies execute from repo root: +- `conan export utils/conan/llvm-core/ --version 15.0.7 --user secure-software-engineering` +- `conan export utils/conan/clang/ --version 15.0.7 --user secure-software-engineering` +- `conan export .` +- View exported `conan list "phasar/*"` +- [Consume the package](https://docs.conan.io/2/tutorial/consuming_packages.html) + +If you just want to use phasar-cli: +- `conan install --tool-requires phasar/... --build=missing -of .` +- `source conanbuild.sh` +- `phasar-cli --help` + +## 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! + ### Installing PhASAR's Git pre-commit hook You are very much welcome to contribute to the PhASAR project. diff --git a/bootstrap.sh b/bootstrap.sh index bb641b5da8..9d0a911acc 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -6,10 +6,10 @@ source ./utils/safeCommandsSet.sh readonly PHASAR_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" PHASAR_INSTALL_DIR="/usr/local/phasar" -LLVM_INSTALL_DIR="/usr/local/llvm-14" +LLVM_INSTALL_DIR="/usr/local/llvm-15" NUM_THREADS=$(nproc) -LLVM_RELEASE=llvmorg-14.0.6 +LLVM_RELEASE=llvmorg-15.0.7 DO_UNIT_TEST=true DO_INSTALL=false BUILD_TYPE=Release @@ -108,7 +108,7 @@ set -- "${POSITIONAL[@]}" # restore positional parameters echo "installing phasar dependencies..." if [ -x "$(command -v pacman)" ]; then - yes | sudo pacman -Syu --needed which zlib sqlite3 python3 doxygen gcc python-pip ninja cmake + yes | sudo pacman -Syu --needed which zlib python3 doxygen gcc ninja cmake else ./utils/InstallAptDependencies.sh fi @@ -146,7 +146,7 @@ else # install missing packages if necessary boostlibnames=("libboost-graph") additional_boost_libs=() - for boost_lib in ${boostlibnames[@]}; do + for boost_lib in "${boostlibnames[@]}"; do dpkg -s "$boost_lib${DESIRED_BOOST_VERSION}" >/dev/null 2>&1 || dpkg -s "$boost_lib${DESIRED_BOOST_VERSION}.0" >/dev/null 2>&1 || additional_boost_libs+=("$boost_lib${DESIRED_BOOST_VERSION}") || @@ -163,7 +163,7 @@ fi # installing LLVM tmp_dir=$(mktemp -d "llvm-build.XXXXXXXX" --tmpdir) -./utils/install-llvm.sh "${NUM_THREADS}" "${tmp_dir}" ${LLVM_INSTALL_DIR} ${LLVM_RELEASE} +./utils/install-llvm.sh "${NUM_THREADS}" "${tmp_dir}" "${LLVM_INSTALL_DIR}" ${LLVM_RELEASE} rm -rf "${tmp_dir}" echo "dependencies successfully installed" @@ -193,7 +193,8 @@ if ${DO_UNIT_TEST}; then NUM_FAILED_TESTS=0 pushd unittests - for x in $(find . -type f -executable -print); do + mapfile -t files < <(find . -type f -executable) + for x in "${files[@]}"; do pushd "${x%/*}" && ./"${x##*/}" || { echo "Test ${x} failed."; NUM_FAILED_TESTS=$((NUM_FAILED_TESTS+1)); }; popd; done @@ -206,13 +207,13 @@ fi if ${DO_INSTALL}; then echo "install phasar..." - sudo cmake -DCMAKE_INSTALL_PREFIX=${PHASAR_INSTALL_DIR} -P cmake_install.cmake + sudo cmake -DCMAKE_INSTALL_PREFIX="${PHASAR_INSTALL_DIR}" -P cmake_install.cmake sudo ldconfig safe_cd .. echo "phasar successfully installed to ${PHASAR_INSTALL_DIR}" echo "Set environment variables" - ./utils/setEnvironmentVariables.sh ${LLVM_INSTALL_DIR} ${PHASAR_INSTALL_DIR} + ./utils/setEnvironmentVariables.sh "${LLVM_INSTALL_DIR}" "${PHASAR_INSTALL_DIR}" fi echo "done." diff --git a/cmake/add_llvm.cmake b/cmake/add_llvm.cmake new file mode 100644 index 0000000000..5ec7e966bf --- /dev/null +++ b/cmake/add_llvm.cmake @@ -0,0 +1,105 @@ +macro(add_llvm) + + if (NOT PHASAR_IN_TREE) + # Only search for LLVM if we build out of tree + find_package(LLVM ${PHASAR_LLVM_VERSION} REQUIRED CONFIG) + find_library(LLVM_LIBRARY NAMES LLVM PATHS ${LLVM_LIBRARY_DIRS} NO_DEFAULT_PATH) + + 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 (PHASAR_USE_CONAN) + set(LLVM_ENABLE_RTTI ON) + elseif (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() + 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() + +endmacro() + +macro(add_clang) + # The clang-cpp shared library is now the preferred way to link dynamically against libclang if we build out of tree. + if (PHASAR_USE_CONAN) + find_package(Clang REQUIRED) + set(CLANG_LIBRARY clangCodeGen clangTooling) + elseif(NOT PHASAR_IN_TREE) + if (USE_LLVM_FAT_LIB) + find_library(CLANG_LIBRARY NAMES clang-cpp libclang-cpp HINTS ${LLVM_LIBRARY_DIRS}) + else() + find_library(CLANG_LIBRARY NAMES clangCodeGen clangTooling HINTS ${LLVM_LIBRARY_DIRS}) + endif() + 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() + + 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() +endmacro(add_clang) diff --git a/cmake/add_nlohmann_json.cmake b/cmake/add_nlohmann_json.cmake new file mode 100644 index 0000000000..b15afc1b5e --- /dev/null +++ b/cmake/add_nlohmann_json.cmake @@ -0,0 +1,73 @@ + +function(add_nlohmann_json) + if (PHASAR_USE_CONAN) + find_package(nlohmann_json REQUIRED) + else() + set(JSON_BuildTests OFF) + set(JSON_Install OFF) + + if (PHASAR_IN_TREE) + set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS nlohmann_json) + endif() + + add_subdirectory(external/json EXCLUDE_FROM_ALL) + set_property(TARGET nlohmann_json APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES $ + ) + + install(TARGETS nlohmann_json + EXPORT ${PHASAR_DEPS_EXPORT_SET} + LIBRARY DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/lib + ARCHIVE DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/lib + RUNTIME DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/bin + ) + install(DIRECTORY external/json/include/ + DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/include + ) + endif() +endfunction() + +function(add_json_schema_validator) + if (PHASAR_USE_CONAN) + find_package(nlohmann_json_schema_validator REQUIRED) + else() + # 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 + # in the installed state the nlohmann_json_schema_validator needs the nlohmann_json package which needs + # to be installed. + # The following workaround may collapse or become unnecessary once the issue is + # changed or fixed in nlohmann_json_schema_validator. + if (PHASAR_IN_TREE) + set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS nlohmann_json_schema_validator) + endif() + + set(JSON_VALIDATOR_INSTALL OFF) + + set(BUILD_SHARED_LIBS_SAVE ${BUILD_SHARED_LIBS}) + set(BUILD_SHARED_LIBS OFF) + + add_subdirectory(external/json-schema-validator EXCLUDE_FROM_ALL) + + set(BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS_SAVE}) + + set_property(TARGET nlohmann_json_schema_validator APPEND PROPERTY + INTERFACE_INCLUDE_DIRECTORIES $ + ) + + # Silence warning that we do not install the PUBLIC_HEADER target property. + # We can't, since it contains a relative path located from deep inside the schema validator tree + set_target_properties(nlohmann_json_schema_validator PROPERTIES PUBLIC_HEADER "") + + install(TARGETS nlohmann_json_schema_validator + EXPORT ${PHASAR_DEPS_EXPORT_SET} + LIBRARY DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/lib + ARCHIVE DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/lib + RUNTIME DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/bin + # PUBLIC_HEADER DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/include/nlohmann + ) + install(FILES external/json-schema-validator/src/nlohmann/json-schema.hpp + DESTINATION ${PHASAR_DEPS_INSTALL_DESTINATION}/include/nlohmann + ) + endif() +endfunction() diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 8499a4905e..c991f508db 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -26,7 +26,7 @@ function(add_phasar_unittest test_name) target_link_libraries(${test} PRIVATE phasar - gtest + GTest::gtest ) add_test(NAME "${test}" @@ -37,11 +37,98 @@ function(add_phasar_unittest test_name) set(CTEST_OUTPUT_ON_FAILURE ON) endfunction() +function(validate_binary_version result item) + message(STATUS " - validate_binary_version of \"${item}\"") + execute_process( + COMMAND "${item}" --version + OUTPUT_VARIABLE output + ERROR_VARIABLE error + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + string(REGEX MATCH " version ${PHASAR_LLVM_VERSION}\\." match "${output}") + if (match) + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + function(generate_ll_file) set(options MEM2REG DEBUG O1 O2 O3) set(testfile FILE) cmake_parse_arguments(GEN_LL "${options}" "${testfile}" "" ${ARGN}) + if (NOT clang) + # Conan deps are available in in PATH + set(default_llvm "${LLVM_TOOLS_BINARY_DIR}" ) + set(fallback_llvm "${Clang_INCLUDE_DIR}/../bin" "${LLVM_INCLUDE_DIR}/../bin") + set(user_compiled_llvm "/usr/local/llvm-${PHASAR_LLVM_VERSION}/bin") + set(package_manager_llvm "/usr/lib/llvm-${PHASAR_LLVM_VERSION}/bin/") + foreach(hint ${default_llvm} ${fallback_llvm} ${user_compiled_llvm} ${package_manager_llvm}) + if ("${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.20") + cmake_path(NORMAL_PATH hint OUTPUT_VARIABLE hint) + endif() + list(APPEND binary_hint_paths "${hint}") + endforeach() + message(STATUS "HINTS to find clang/clang++/opt: ${binary_hint_paths}") + + if ("${CMAKE_VERSION}" VERSION_LESS "3.25") # VALIDATOR requires it + message(WARNING "I would prefer CMake >= 3.25 but I will try my best to resolve deps.") + find_program(clang REQUIRED + NAMES clang-${PHASAR_LLVM_VERSION} clang + HINTS ${binary_hint_paths}) + find_program(clangcpp REQUIRED + NAMES clang++-${PHASAR_LLVM_VERSION} clang++ + HINTS ${binary_hint_paths}) + find_program(opt REQUIRED + NAMES opt-${PHASAR_LLVM_VERSION}4 opt + HINTS ${binary_hint_paths}) + + set(IS_VALID_VERSION "") + validate_binary_version(IS_VALID_VERSION "${clang}") + if (NOT "${IS_VALID_VERSION}") + set(clang "") + endif() + validate_binary_version(IS_VALID_VERSION "${clangcpp}") + if (NOT "${IS_VALID_VERSION}") + set(clangcpp "") + endif() + validate_binary_version(IS_VALID_VERSION "${opt}") + if (NOT "${IS_VALID_VERSION}") + set(opt "") + endif() + else() + find_program(clang REQUIRED + NAMES clang-${PHASAR_LLVM_VERSION} clang + HINTS ${binary_hint_paths} + VALIDATOR validate_binary_version) + find_program(clangcpp REQUIRED + NAMES clang++-${PHASAR_LLVM_VERSION} clang++ + HINTS ${binary_hint_paths} + VALIDATOR validate_binary_version) + find_program(opt REQUIRED + NAMES opt-${PHASAR_LLVM_VERSION} opt + HINTS ${binary_hint_paths} + VALIDATOR validate_binary_version) + endif() + if ("${clang}" STREQUAL "") + message(FATAL_ERROR "Couldn't find clang in version ${PHASAR_LLVM_VERSION}") + else() + message(STATUS "found clang binary in \"${clang}\"") + endif() + if ("${clangcpp}" STREQUAL "") + message(FATAL_ERROR "Couldn't find clang++ in version ${PHASAR_LLVM_VERSION}") + else() + message(STATUS "found clang++ binary in \"${clangcpp}\"") + endif() + if ("${opt}" STREQUAL "") + message(FATAL_ERROR "Couldn't find opt in version ${PHASAR_LLVM_VERSION}") + else() + message(STATUS "found opt binary in \"${opt}\"") + endif() + endif() + # get file extension get_filename_component(test_code_file_ext ${GEN_LL_FILE} EXT) string(REPLACE "." "_" ll_file_suffix ${test_code_file_ext}) @@ -110,18 +197,18 @@ function(generate_ll_file) # define .ll file generation command if(${test_code_file_ext} STREQUAL ".cpp") - set(GEN_CMD ${CMAKE_CXX_COMPILER_LAUNCHER} ${CMAKE_CXX_COMPILER}) + set(GEN_CMD ${CMAKE_CXX_COMPILER_LAUNCHER} ${clangcpp}) list(APPEND GEN_CMD ${GEN_CXX_FLAGS}) else() - set(GEN_CMD ${CMAKE_C_COMPILER_LAUNCHER} ${CMAKE_C_COMPILER}) + set(GEN_CMD ${CMAKE_C_COMPILER_LAUNCHER} ${clang}) list(APPEND GEN_CMD ${GEN_C_FLAGS}) endif() if(GEN_LL_MEM2REG) - add_custom_command( + add_custom_command( OUTPUT ${test_code_ll_file} COMMAND ${GEN_CMD} ${test_code_file_path} -o ${test_code_ll_file} - COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} opt -mem2reg -S ${test_code_ll_file} -o ${test_code_ll_file} + COMMAND ${CMAKE_CXX_COMPILER_LAUNCHER} ${opt} -mem2reg -S ${test_code_ll_file} -o ${test_code_ll_file} COMMENT ${GEN_CMD_COMMENT} DEPENDS ${GEN_LL_FILE} VERBATIM @@ -188,6 +275,8 @@ function(add_phasar_library name) EXPORT_NAME ${component_name} ) + target_compile_features(${name} PUBLIC cxx_std_17) + if(LLVM_COMMON_DEPENDS) add_dependencies(${name} ${LLVM_COMMON_DEPENDS}) endif(LLVM_COMMON_DEPENDS) diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000000..af1dfe05bf --- /dev/null +++ b/conanfile.py @@ -0,0 +1,365 @@ +from conan import ConanFile +from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout, CMakeDeps +from conan.tools.build import check_min_cppstd +from conan.tools.files import ( + load, + save, + copy, + rm, +) +from conan.tools.scm import Git +from os.path import join, isdir, exists +import re +from pathlib import Path, PurePosixPath +import textwrap +import json +import os +from conan.errors import ConanException + +required_conan_version = ">=2.0" + +def components_from_dotfile(dotfile): + def node_labels(dot): + # here we are mapping dependencies visible to cmake to component dependencies in conan + label_replacements = { + "LibXml2::LibXml2": "libxml2::libxml2", + "ZLIB::ZLIB": "zlib::zlib", + "zstd::libzstd_static": "zstd::zstdlib", + "-lpthread": "pthread", + "curl": "libcurl::libcurl", + "nlohmann_json_schema_validator": "json-schema-validator::json-schema-validator", + "clangCodeGen": "clang::clangCodeGen", + "clangTooling": "clang::clangTooling", + "SQLite::SQLite3": "sqlite3::sqlite3", + } + for row in dot: + # e.g. "node0" [ label = "phasar\n(phasar::phasar)", shape = octagon ]; + match_label = re.match(r'''^\s*"(node[0-9]+)"\s*\[\s*label\s*=\s*"([^\\"]+)''', row) + if match_label: + node = match_label.group(1) + label = match_label.group(2) + if label.startswith("LLVM"): + yield node, f"llvm-core::{label}" + # XXX find_library adds direct filepath -> imho a flaw in current cmake files + elif label.endswith("libsqlite3.a"): + yield node, "sqlite3::sqlite3" + elif label.endswith("libclang-cpp.so"): + yield node, "clang::clang" + elif label.endswith("libclangCodeGen.a"): + yield node, "clang::clangCodeGen" + elif label.endswith("libclangTooling.a"): + yield node, "clang::clangTooling" + else: + yield node, label_replacements.get(label, label) + + def node_dependencies(dot): + ignore_deps = [ + ] + labels = {k: v for k, v in node_labels(dot)} + for row in dot: + # "node0" -> "node1" [ style = dashed ] // phasar -> LLVMAnalysis + match_dep = re.match(r'''^\s*"(node[0-9]+)"\s*->\s*"(node[0-9]+)".*''', row) + if match_dep: + node_label = labels[match_dep.group(1)] + dependency = labels[match_dep.group(2)] + if node_label.startswith("phasar") and PurePosixPath(dependency).parts[-1] not in ignore_deps: + yield node_label, labels[match_dep.group(2)] + # some components don't have dependencies + for label in labels.values(): + if label.startswith("phasar"): + yield label, None + + system_libs = { + "ole32", + "delayimp", + "shell32", + "advapi32", + "-delayload:shell32.dll", + "uuid", + "psapi", + "-delayload:ole32.dll", + "ntdll", + "ws2_32", + "rt", + "m", + "dl", + "pthread", + "stdc++fs" + } + components = {} + dotfile_rows = dotfile.split("\n") + for node, dependency in node_dependencies(dotfile_rows): + if dependency in system_libs: + key = "system_libs" + elif dependency is not None and (dependency.startswith("phasar") or "::" in dependency): + key = "requires" + else: + key = "unknown" + if node not in components: + components[node] = { "system_libs": [], "requires": [], "unknown": [] } + if dependency is not None: + components[node][key] = [dependency] + elif dependency is not None: + components[node][key].append(dependency) + + return components + +class PhasarRecipe(ConanFile): + name = "phasar" + package_type = "library" + + # Optional metadata + license = "MIT license" + url = "https://github.com/secure-software-engineering/phasar" + description = "A LLVM-based static analysis framework. " + topics = ("LLVM", "PhASAR", "SAST") + + # Binary configuration + settings = "os", "compiler", "build_type", "arch" + options = { + "with_z3": [True, False], + "shared": [True, False], + "fPIC": [True, False], + "tests": [True, False], + "llvm_version": ["ANY"], + } + default_options = { + "with_z3": True, + "shared": False, + "fPIC": True, + "tests": False, + "llvm_version": "15.0.7" + } + + def _parse_gitignore(self, folder, additional_exclusions = [], invert=False): + exclusions = [] + inclusions = [] + if invert: + for exc in additional_exclusions: + if exc.startswith("!"): + inclusions = exc[1:] + else: + inclusions = f"!{exc}" + else: + exclusions = additional_exclusions + + with open(f'{folder}/.gitignore', 'r') as file: + for line in file: + line = line.strip() + if line.startswith("#") or not line: + continue + if invert: + if line.startswith("!"): + inclusions.append(line[1:]) + else: + inclusions.append("!" + line) + else: + exclusions.append(line) + + if invert: + return inclusions + else: + return exclusions + + def export_sources(self): + exclusions = self._parse_gitignore(".", [ + "test_package", + "utils", + "img", + "githooks", + "external" + ]) + + for tlf in os.listdir("."): + if isdir(tlf): + copy(self, f"{tlf}/*", src=".", dst=self.export_sources_folder, excludes=exclusions) + else: + copy(self, tlf, src=".", dst=self.export_sources_folder, excludes=exclusions) + + @property + def _graphviz_file(self): + return PurePosixPath(self.build_folder) / "graph" / "phasar.dot" + + @property + def _info_file(self): + # this is called very early where folders aren't set but this is fine + if self.export_folder is None: + return None + return PurePosixPath(self.export_folder) / "info.json" + + def _read_info(self): + if self._info_file is not None and exists(self._info_file): + with open(self._info_file, encoding="utf-8") as fp: + return json.load(fp) + else: + return { + "version": None, + } + + def _write_info(self, info): + if self._info_file is not None: + with open(self._info_file, "w", encoding="utf-8") as fp: + json.dump(info, fp, indent=2) + + def set_version(self): + if self.version is not None: + return + info = self._read_info() + version = info["version"] + if version is None: + git = Git(self, self.recipe_folder) + # XXX consider git.coordinates_to_conandata() + if git.is_dirty(): + raise ConanException("Repository is dirty. I can't calculate a correct version and this is a potential leak because all files visible to git will be exported. Please stash or commit, to skip this for local testing use \"--version dev\".") + self.output.info("No version information set, retrieving from git.") + calver = git.run("show -s --date=format:'%Y.%m.%d' --format='%cd'") + short_hash = git.run("show -s --format='%h'") + version = f"{calver}+{short_hash}" + if info["version"] != version: + info["version"] = version + self._write_info(info) + self.version = version + + def layout(self): + cmake_layout(self) + + def config_options(self): + if self.settings.os == "Windows": + self.options.rm_safe("fPIC") + + def requirements(self): + self.requires("boost/[>1.72.0 <=1.86.0]") + self.requires("sqlite3/[>=3 <4]") + self.requires(f"clang/{self.options.llvm_version}@secure-software-engineering", transitive_libs=True, transitive_headers=True) + self.requires("nlohmann_json/3.11.3", transitive_headers=True) + self.requires("json-schema-validator/2.3.0", transitive_libs=True, transitive_headers=True) + + llvm_options={ + "rtti": True, + } + if self.options.with_z3: + self.requires("z3/[>=4.7.1 <5]") + llvm_options["with_z3"] = True + self.requires(f"llvm-core/{self.options.llvm_version}@secure-software-engineering", transitive_libs=True, transitive_headers=True, options=llvm_options) + + def build_requirements(self): + self.tool_requires("cmake/[>=3.25.0 <4.0.0]") # find_program validator + self.tool_requires("ninja/[>=1.9.0 <2.0.0]") + if self.options.tests: + self.test_requires("openssl/[>2 <4]") + self.test_requires("gtest/1.14.0") + + def configure(self): + if self.options.shared: + self.options.rm_safe("fPIC") + + def validate(self): + check_min_cppstd(self, '17') + + def generate(self): + deps = CMakeDeps(self) + deps.generate() + tc = CMakeToolchain(self, 'Ninja') + tc.generate() + + def _cmake_configure(self): + cmake = CMake(self) + self._handle_graphviz() + cmake.configure( + variables={ + 'PHASAR_ENABLE_PIC': self.options.get_safe("fPIC", False), + 'PHASAR_USE_CONAN': True, + 'BUILD_SHARED_LIBS': self.options.shared, + 'PHASAR_BUILD_UNITTESTS': self.options.tests, + 'PHASAR_BUILD_IR': self.options.tests, + 'PHASAR_BUILD_DOC': False, + 'PHASAR_USE_Z3': self.options.with_z3, + 'USE_LLVM_FAT_LIB': False, + 'BUILD_PHASAR_CLANG': True, + 'PHASAR_BUILD_TOOLS': True, + }, + cli_args=[ + f"--graphviz={self._graphviz_file}" + ] + ) + return cmake + + def _handle_graphviz(self): + exclude_patterns = [ + "LLVMTableGenGlobalISel.*", + "CONAN_LIB.*", + "LLVMExegesis.*", + "LLVMCFIVerify.*" + ] + graphviz_options = textwrap.dedent(f""" + set(GRAPHVIZ_EXECUTABLES OFF) + set(GRAPHVIZ_MODULE_LIBS OFF) + set(GRAPHVIZ_OBJECT_LIBS OFF) + set(GRAPHVIZ_IGNORE_TARGETS "{';'.join(exclude_patterns)}") + """) + save(self, PurePosixPath(self.build_folder) / "CMakeGraphVizOptions.cmake", graphviz_options) + + def build(self): + cmake = self._cmake_configure() + cmake.build() + if self.options.tests: + cmake.ctest(cli_args=[ + "--exclude-regex 'IDEExtendedTaintAnalysisTest.*'", # known flaky test + "--no-tests=error", + "--output-on-failure", + "--test-dir", + self.build_folder + ]) + + @property + def _cmake_module_path(self): + return PurePosixPath("lib") / "cmake" / "phasar" + + @property + def _build_info_file(self): + return PurePosixPath(self.package_folder) / self._cmake_module_path / "conan_phasar_build_info.json" + + def _write_build_info(self): + # maybe process original config + cmake_config = Path(self.package_folder / self._cmake_module_path / "phasarConfig.cmake").read_text("utf-8") + components = components_from_dotfile(load(self, self._graphviz_file)) + + build_info = { + "components": components, + } + + with open(self._build_info_file, "w", encoding="utf-8") as fp: + json.dump(build_info, fp, indent=2) + + return build_info + + def _read_build_info(self) -> dict: + with open(self._build_info_file, encoding="utf-8") as fp: + return json.load(fp) + + def package_id(self): + del self.info.options.tests + + def package(self): + copy(self, "LICENSE.txt", self.source_folder, join(self.package_folder, "licenses")) + + cmake = self._cmake_configure() + cmake.install() + rm(self, "phasarConfig*.cmake", join("lib", "cmake", "phasar")) + rm(self, "*target*.cmake", join("lib", "cmake", "phasar")) + + self._write_build_info() + + def package_info(self): + self.cpp_info.set_property("cmake_file_name", "phasar") + + interfaces = ["phasar_interface"] + + build_info = self._read_build_info() + components = build_info["components"] + + for component_name, data in components.items(): + self.cpp_info.components[component_name].set_property("cmake_target_name", component_name) + self.cpp_info.components[component_name].libs = [component_name] if component_name not in interfaces else [] + self.cpp_info.components[component_name].requires = data["requires"] + self.cpp_info.components[component_name].system_libs = data["system_libs"] diff --git a/config.h.in b/config.h.in index 923329206c..97b0e8a9f6 100644 --- a/config.h.in +++ b/config.h.in @@ -2,6 +2,8 @@ #define PHASAR_CONFIG_CONFIG_H #define PHASAR_CONFIG_DIR "@PHASAR_CONFIG_INSTALL_DIR@" +#define PHASAR_VERSION @PHASAR_VERSION@ +#define PHASAR_VERSION_STRING "v@PHASAR_VERSION@" #cmakedefine PAMM_CORE #cmakedefine PAMM_FULL @@ -9,4 +11,6 @@ #cmakedefine DYNAMIC_LOG #cmakedefine BUILD_PHASAR_CLANG +#cmakedefine PHASAR_HAS_SQLITE + #endif /* PHASAR_CONFIG_CONFIG_H */ diff --git a/config/double-free-config.json b/config/double-free-config.json new file mode 100644 index 0000000000..043b392288 --- /dev/null +++ b/config/double-free-config.json @@ -0,0 +1,28 @@ +{ + "name": "double-free", + "version": 1.0, + "functions": [ + { + "name": "free", + "params": { + "source": [ + 0 + ], + "sink": [ + 0 + ] + } + }, + { + "name": "_ZdlPv", + "params": { + "source": [ + 0 + ], + "sink": [ + 0 + ] + } + } + ] +} diff --git a/config/glibc_function_list_v1-04.05.17.conf b/config/glibc_function_list_v1-04.05.17.conf index dc6ecbbfdb..367be34fc8 100644 --- a/config/glibc_function_list_v1-04.05.17.conf +++ b/config/glibc_function_list_v1-04.05.17.conf @@ -1,6 +1,6 @@ -*pthread_getspecific -*sbrk -*sem_open +*pthread_getspecific //return the value currently bound to the specified key on behalf of the calling thread +*sbrk //input is the new address for programm break. first position after data segment. returns 0 or 1 +*sem_open //returns address of new semaphore or error _exit _Exit _flushlbf @@ -29,8 +29,8 @@ __va_copy a64l abort abs -accept -access +accept //sockets +access //file path acos acosf acosh @@ -38,7 +38,7 @@ acoshf acoshl acosl addmntent -addseverity +addseverity //? adjtime adjtimex aio_cancel @@ -56,17 +56,17 @@ aio_suspend aio_suspend64 aio_write aio_write64 -alarm +alarm //only previos alarm values? aligned_alloc alloca alphasort alphasort64 -argp_error -argp_failure -argp_help -argp_parse -argp_state_help -argp_usage +argp_error //? +argp_failure //? +argp_help //? +argp_parse //? +argp_state_help //? +argp_usage //? argz_add argz_add_sep argz_append @@ -79,7 +79,7 @@ argz_insert argz_next argz_replace argz_stringify -asctime +asctime //from struct to return asctime_r asin asinf @@ -100,7 +100,7 @@ atanhf atanhl atanl atexit -atof +atof //in -> return atoi atol atoll @@ -128,10 +128,10 @@ cacoshf cacoshl cacosl calloc -canonicalize -canonicalizef -canonicalizel -canonicalize_file_name +canonicalize //? +canonicalizef //? +canonicalizel //? +canonicalize_file_name //? carg cargf cargl @@ -198,7 +198,7 @@ conj conjf conjl connect -continue +continue //? copysign copysignf copysignl @@ -246,7 +246,7 @@ ctime_r cuserid dcgettext dcngettext -DES_FAILED +DES_FAILED //? des_setparity dgettext difftime @@ -292,8 +292,8 @@ erfcf erfcl erff erfl -err -error +err //? +error //? error_at_line errx execl @@ -370,9 +370,9 @@ fgetpos64 fgetpwent fgetpwent_r fgets -fgets_unlocked +fgets_unlocked //? fgetwc -fgetwc_unlocked +fgetwc_unlocked //? fgetws fgetws_unlocked fileno @@ -503,12 +503,12 @@ getitimer getline getloadavg getlogin -getmntent +getmntent //? getmntent_r getnetbyaddr getnetbyname getnetent -getnetgrent +getnetgrent //? getnetgrent_r getopt getopt_long @@ -529,7 +529,7 @@ getprotobynumber getprotoent getpt getpwent -getpwent_r +getpwent_r //? getpwnam getpwnam_r getpwuid @@ -581,7 +581,7 @@ gmtime_r grantpt grantpt gsignal -gtty +gtty //? hasmntopt hcreate hcreate_r @@ -597,7 +597,7 @@ hypotl iconv iconv_close iconv_open -IFTODT +IFTODT //? if_freenameindex if_indextoname if_nameindex @@ -750,11 +750,11 @@ lstat lstat64 lutimes madvise -main +main //? makecontext mallinfo -malloc -mallopt +malloc //? +mallopt //? matherr mblen mbrlen @@ -776,7 +776,7 @@ memmove mempcpy memrchr memset -merge +merge //? mkdir mkdtemp mkfifo @@ -826,13 +826,13 @@ nftw64 ngettext nice nl_langinfo -notfound +notfound //? nrand48 nrand48_r ntohl ntohs -ntp_adjtime -ntp_gettime +ntp_adjtime //? +ntp_gettime //? obstack_1grow obstack_1grow_fast obstack_alignment_mask @@ -927,7 +927,7 @@ rand_r rawmemchr read readdir -readdir64 +readdir64 //? readdir64_r readdir_r readlink @@ -971,9 +971,9 @@ scalbn scalbnf scalbnl scandir -scandir64 +scandir64 //? scanf -sched_getaffinity +sched_getaffinity //?? sched_getparam sched_getscheduler sched_get_priority_max @@ -987,7 +987,7 @@ secure_getenv seed48 seed48_r seekdir -select +select //? semctl semget semop @@ -1020,7 +1020,7 @@ sethostname setitimer setjmp setkey -setkey_r +setkey_r //? setlinebuf setlocale setlogmask @@ -1033,7 +1033,7 @@ setpayloadl setpayloadsig setpayloadsigf setpayloadsigl -setpgid +setpgid //? setpgrp setpriority setprotoent @@ -1120,7 +1120,7 @@ strdup strdupa strerror strerror_r -strfmon +strfmon //? strfromd strfromf strfroml @@ -1149,7 +1149,7 @@ strtok_r strtol strtold strtoll -strtoq +strtoq //? strtoul strtoull strtoumax @@ -1170,16 +1170,16 @@ sysctl syslog system sysv_signal -S_ISBLK -S_ISCHR -S_ISDIR -S_ISFIFO -S_ISLNK -S_ISREG -S_ISSOCK -S_TYPEISMQ -S_TYPEISSEM -S_TYPEISSHM +S_ISBLK //? +S_ISCHR //? +S_ISDIR //? +S_ISFIFO //? +S_ISLNK //? +S_ISREG //? +S_ISSOCK //? +S_TYPEISMQ //? +S_TYPEISSEM //? +S_TYPEISSHM //? tan tanf tanh @@ -1192,20 +1192,20 @@ tcflush tcgetattr tcgetpgrp tcgetsid -tcsendbreak -tcsetattr +tcsendbreak //? +tcsetattr //? tcsetpgrp tdelete tdestroy telldir tempnam -TEMP_FAILURE_RETRY +TEMP_FAILURE_RETRY //? textdomain tfind tgamma tgammaf tgammal -time +time //? timegm timelocal times @@ -1215,12 +1215,12 @@ tmpnam tmpnam_r toascii tolower -totalorder -totalorderf -totalorderl -totalordermag -totalordermagf -totalordermagl +totalorder //conditional data flow +totalorderf //conditional data flow +totalorderl //conditional data flow +totalordermag //conditional data flow +totalordermagf //conditional data flow +totalordermagl //conditional data flow toupper towctrans towlower @@ -1232,8 +1232,8 @@ truncf truncl tryagain tsearch -ttyname -ttyname_r +ttyname //? +ttyname_r //? twalk tzset ufromfp @@ -1247,7 +1247,7 @@ umask umount umount2 uname -unavail +unavail //? ungetc ungetwc unlink @@ -1257,15 +1257,15 @@ updwtmp utime utimes utmpname -utmpxname +utmpxname //not specified valloc vasprintf -va_arg +va_arg //? va_copy va_end va_start -verr -verrx +verr //? +verrx //? versionsort versionsort64 vfork @@ -1273,7 +1273,7 @@ vfprintf vfscanf vfwprintf vfwscanf -vlimit +vlimit //very old vprintf vscanf vsnprintf @@ -1281,8 +1281,8 @@ vsprintf vsscanf vswprintf vswscanf -vsyslog -vtimes +vsyslog //? +vtimes //very old vwarn vwarnx vwprintf @@ -1300,13 +1300,13 @@ wcrtomb wcscasecmp wcscat wcschr -wcschrnul +wcschrnul //? wcscmp wcscoll wcscpy wcscspn wcsdup -wcsftime +wcsftime //? wcslen wcsncasecmp wcsncat @@ -1327,18 +1327,18 @@ wcstol wcstold wcstoll wcstombs -wcstoq +wcstoq //? wcstoul wcstoull wcstoumax -wcstouq -wcswcs +wcstouq //? +wcswcs //? wcsxfrm wctob wctomb wctrans wctype -WEXITSTATUS +WEXITSTATUS //? WIFEXITED WIFSIGNALED WIFSTOPPED @@ -1351,7 +1351,7 @@ wmemset wordexp wordfree wprintf -write +write //? writev wscanf WSTOPSIG diff --git a/docs/Doxyfile.in b/docs/Doxyfile.in index dbf9245f07..b45f14eebd 100644 --- a/docs/Doxyfile.in +++ b/docs/Doxyfile.in @@ -32,19 +32,19 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Phasar" +PROJECT_NAME = "PhASAR" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.1 +PROJECT_NUMBER = @PHASAR_VERSION@ # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Phasar a LLVM-based Static Analysis Framework" +PROJECT_BRIEF = "PhASAR a LLVM-based Static Analysis Framework" # With the PROJECT_LOGO tag one can specify an logo or icon that is included in # the documentation. The maximum height of the logo should not exceed 55 pixels @@ -234,7 +234,7 @@ TCL_SUBST = # members will be omitted, etc. # The default value is: NO. -OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored @@ -298,7 +298,7 @@ AUTOLINK_SUPPORT = YES # diagrams that involve STL classes more complete and accurate. # The default value is: NO. -BUILTIN_STL_SUPPORT = NO +BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. @@ -424,7 +424,7 @@ EXTRACT_STATIC = NO # for Java sources. # The default value is: YES. -EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_CLASSES = NO # This flag is only useful for Objective-C code. When set to YES local methods, # which are defined in the implementation section but not in the interface are @@ -513,7 +513,7 @@ SHOW_GROUPED_MEMB_INC = NO # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. -FORCE_LOCAL_INCLUDES = NO +FORCE_LOCAL_INCLUDES = YES # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. @@ -892,7 +892,7 @@ USE_MDFILE_AS_MAINPAGE = # also VERBATIM_HEADERS is set to NO. # The default value is: NO. -SOURCE_BROWSER = NO +SOURCE_BROWSER = YES # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. @@ -911,7 +911,7 @@ STRIP_CODE_COMMENTS = YES # function all documented functions referencing it will be listed. # The default value is: NO. -REFERENCED_BY_RELATION = NO +REFERENCED_BY_RELATION = YES # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. diff --git a/docs/README.dox b/docs/README.dox index 3113dfdaed..e00ef59771 100644 --- a/docs/README.dox +++ b/docs/README.dox @@ -1,6 +1,6 @@ /** -@mainpage Phasar a LLVM-based Static Analysis Framework +@mainpage PhASAR: A LLVM-based Static Analysis Framework @author Philipp Schubert (E-Mail: philipp.schubert@upb.de) and others diff --git a/examples/use-phasar-as-library/CMakeLists.txt b/examples/use-phasar-as-library/CMakeLists.txt index a7bfaa8d96..5246e3b823 100644 --- a/examples/use-phasar-as-library/CMakeLists.txt +++ b/examples/use-phasar-as-library/CMakeLists.txt @@ -17,7 +17,7 @@ add_executable(myphasartool myphasartool.cpp ) -# Old way using phasar_config: +# Old way using phasar_config (deprecated): # phasar_config(myphasartool) # New way using target_link_libraries: diff --git a/examples/use-phasar-as-library/README.md b/examples/use-phasar-as-library/README.md index 44dbce6ea8..5f5f6c1828 100644 --- a/examples/use-phasar-as-library/README.md +++ b/examples/use-phasar-as-library/README.md @@ -4,3 +4,5 @@ This small example shows how you can setup a CMake project that uses PhASAR as a 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. + +To use phasar from a custom install location, you may specify the `phasar_ROOT` CMake variable to point to phasar's install directory. diff --git a/examples/use-phasar-with-fetch-content/CMakeLists.txt b/examples/use-phasar-with-fetch-content/CMakeLists.txt new file mode 100644 index 0000000000..c6e7ce876c --- /dev/null +++ b/examples/use-phasar-with-fetch-content/CMakeLists.txt @@ -0,0 +1,38 @@ +cmake_minimum_required(VERSION 3.12) + +project(PhasarExttoolTest) + +set(CMAKE_EXPORT_COMPILE_COMMANDS YES) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +include(FetchContent) + +FetchContent_Declare( + phasar + GIT_REPOSITORY https://github.com/secure-software-engineering/phasar.git + GIT_TAG development # At best, use a tagged version, such as v2503 + EXCLUDE_FROM_ALL + OVERRIDE_FIND_PACKAGE +) + +find_package(phasar REQUIRED) + +# Build a small test tool to show how phasar may be used +add_executable(myphasartool + myphasartool.cpp +) + +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 + LIBRARY DESTINATION lib + ARCHIVE DESTINATION lib +) diff --git a/examples/use-phasar-with-fetch-content/README.md b/examples/use-phasar-with-fetch-content/README.md new file mode 100644 index 0000000000..46ea387b0d --- /dev/null +++ b/examples/use-phasar-with-fetch-content/README.md @@ -0,0 +1,5 @@ +# Use PhASAR as a Library via FetchContent + +This small example shows how you can setup a CMake project that uses PhASAR as a library. +This guide does not assume that you have installed PhASAR already. +Instead it will download PhASAR at configure-time and build the necessary parts together with your project as-if it was a sub-module. diff --git a/examples/use-phasar-with-fetch-content/myphasartool.cpp b/examples/use-phasar-with-fetch-content/myphasartool.cpp new file mode 120000 index 0000000000..f1171d5dcc --- /dev/null +++ b/examples/use-phasar-with-fetch-content/myphasartool.cpp @@ -0,0 +1 @@ +../../tools/example-tool/myphasartool.cpp \ No newline at end of file diff --git a/external/googletest b/external/googletest deleted file mode 160000 index b796f7d446..0000000000 --- a/external/googletest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b796f7d44681514f58a683a3a71ff17c94edb0c1 diff --git a/external/json b/external/json index bc889afb4c..9cca280a4d 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit bc889afb4c5bf1c0d8ee29ef35eaaf4c8bef8a5d +Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 diff --git a/external/json-schema-validator b/external/json-schema-validator index 491ac44026..349cba9f7e 160000 --- a/external/json-schema-validator +++ b/external/json-schema-validator @@ -1 +1 @@ -Subproject commit 491ac44026e08f31790f5cacffa62e168bb35e32 +Subproject commit 349cba9f7e3cb423bbc1811bdd9f6770f520b468 diff --git a/include/phasar.h b/include/phasar.h index 0444cf20fa..397cbba0f1 100644 --- a/include/phasar.h +++ b/include/phasar.h @@ -13,7 +13,6 @@ #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" diff --git a/include/phasar/Config.h b/include/phasar/Config.h index 81df120eb8..bf78a9ae25 100644 --- a/include/phasar/Config.h +++ b/include/phasar/Config.h @@ -11,6 +11,6 @@ #define PHASAR_CONFIG_H #include "phasar/Config/Configuration.h" -#include "phasar/Config/Version.h" +#include "phasar/Config/phasar-config.h" #endif // PHASAR_CONFIG_H diff --git a/include/phasar/Config/Configuration.h b/include/phasar/Config/Configuration.h index 86528c3386..1d0f91b789 100644 --- a/include/phasar/Config/Configuration.h +++ b/include/phasar/Config/Configuration.h @@ -17,13 +17,10 @@ #ifndef PHASAR_CONFIG_CONFIGURATION_H_ #define PHASAR_CONFIG_CONFIGURATION_H_ -#include "phasar/Config/Version.h" - #include "llvm/ADT/StringRef.h" #include "llvm/ADT/iterator_range.h" #include "llvm/Support/MemoryBuffer.h" -#include #include #include #include @@ -32,7 +29,8 @@ namespace psr { class PhasarConfig { public: - /// Current Phasar version + /// Current Phasar version. Same as the preprocessor-symbol + /// PHASAR_VERSION_STRING // NOLINTNEXTLINE(readability-identifier-naming) [[nodiscard]] static llvm::StringRef PhasarVersion() noexcept; @@ -66,27 +64,30 @@ class PhasarConfig { [[nodiscard]] std::optional readConfigFileAsTextOrNull(const llvm::Twine &FileName); - /// Specifies the directory in which Phasar is located. + /// Specifies the directory in which Phasar's sources are located. // NOLINTNEXTLINE(readability-identifier-naming) [[nodiscard]] static llvm::StringRef PhasarDirectory() noexcept; /// Name of the file storing all standard header search paths used for /// compilation. - [[nodiscard]] static constexpr llvm::StringRef + [[nodiscard, deprecated("This ancient API is broken and should not be used " + "anymore")]] static constexpr llvm::StringRef // NOLINTNEXTLINE(readability-identifier-naming) HeaderSearchPathsFileName() noexcept { return "standard_header_paths.conf"; } /// Name of the compile_commands.json file (in case we wish to rename) - [[nodiscard]] static constexpr llvm::StringRef + [[nodiscard, deprecated("This ancient API is broken and should not be used " + "anymore")]] static constexpr llvm::StringRef // NOLINTNEXTLINE(readability-identifier-naming) CompileCommandsJson() noexcept { return "compile_commands.json"; } /// Default Source- and Sink-Functions path - [[nodiscard]] static llvm::StringRef + [[nodiscard, deprecated("This ancient API is broken and should not be used " + "anymore")]] static llvm::StringRef // NOLINTNEXTLINE(readability-identifier-naming) DefaultSourceSinkFunctionsPath() noexcept; @@ -142,24 +143,7 @@ class PhasarConfig { private: PhasarConfig(); - bool loadConfigFileInto(llvm::StringRef FileName, - std::set &Lines); - - void loadGlibcSpecialFunctionNames(); - void loadLLVMSpecialFunctionNames(); - std::set SpecialFuncNames; - - /// Name of the file storing all glibc function names. - static constexpr llvm::StringLiteral GLIBCFunctionListFileName = - "glibc_function_list_v1-04.05.17.conf"; - - /// Name of the file storing all LLVM intrinsic function names. - static constexpr llvm::StringLiteral LLVMIntrinsicFunctionListFileName = - "llvm_intrinsics_function_list_v1-04.05.17.conf"; - - /// Log file directory - static constexpr llvm::StringLiteral LogFileDirectory = "log/"; }; } // namespace psr diff --git a/include/phasar/Config/Version.h b/include/phasar/Config/Version.h index 4f595ee968..c5017b860b 100644 --- a/include/phasar/Config/Version.h +++ b/include/phasar/Config/Version.h @@ -1,6 +1,12 @@ #ifndef PHASAR_CONFIG_VERSION_H #define PHASAR_CONFIG_VERSION_H -#define PHASAR_VERSION v2403 +/// Note: This header is only left for backward compatibility and may be removed +/// in the future + +#pragma GCC warning( \ + "The header Version.h is deprecated. Use 'phasar/Config/phasar-config.h' instead") + +#include "phasar/Config/phasar-config.h" #endif diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index 57e358225d..0ec38d5dcb 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -131,11 +131,12 @@ template class CFGBase { void print(ByConstRef Fun, llvm::raw_ostream &OS) const { self().printImpl(Fun, OS); } - [[nodiscard]] nlohmann::json getAsJson(ByConstRef Fun) const { + [[nodiscard, deprecated("Please use printAsJson() instead")]] nlohmann::json + getAsJson(ByConstRef Fun) const { return self().getAsJsonImpl(Fun); } -private: +protected: Derived &self() noexcept { return static_cast(*this); } const Derived &self() const noexcept { return static_cast(*this); @@ -144,7 +145,7 @@ template class CFGBase { template // NOLINTNEXTLINE(readability-identifier-naming) -constexpr bool is_cfg_v = is_crtp_base_of_v +PSR_CONCEPT is_cfg_v = is_crtp_base_of_v &&std::is_same_v &&std::is_same_v; diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index 590b16964e..d73a1b5783 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -11,18 +11,18 @@ #define PHASAR_CONTROLFLOW_CALLGRAPH_H #include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/ControlFlow/CallGraphData.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 "llvm/IR/Function.h" #include "nlohmann/json.hpp" #include +#include #include #include @@ -58,7 +58,7 @@ class CallGraph : public CallGraphBase> { /// Deserializes a previously computed call-graph template [[nodiscard]] static CallGraph - deserialize(const nlohmann::json &PrecomputedCG, + deserialize(const CallGraphData &PrecomputedCG, FunctionGetter GetFunctionFromName, InstructionGetter GetInstructionFromId); @@ -86,12 +86,33 @@ class CallGraph : public CallGraphBase> { [[nodiscard]] bool empty() const noexcept { return CallersOf.empty(); } + template + void printAsJson(llvm::raw_ostream &OS, FunctionIdGetter GetFunctionId, + InstIdGetter GetInstructionId) const { + CallGraphData CGData; + CGData.FToFunctionVertexTy.reserve(CallersOf.size()); + + for (const auto &[Fun, Callers] : CallersOf) { + auto &JCallers = + CGData.FToFunctionVertexTy[std::invoke(GetFunctionId, Fun)]; + + CGData.FToFunctionVertexTy.reserve(Callers->size()); + for (const auto &CS : *Callers) { + JCallers.push_back(std::invoke(GetInstructionId, CS)); + } + } + + CGData.printAsJson(OS); + } + /// Creates a JSON representation of this call-graph suitable for presistent /// storage. /// Use the ctor taking a json object for deserialization template - [[nodiscard]] nlohmann::json getAsJson(FunctionIdGetter GetFunctionId, - InstIdGetter GetInstructionId) const { + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson(FunctionIdGetter GetFunctionId, + InstIdGetter GetInstructionId) const { nlohmann::json J; for (const auto &[Fun, Callers] : CallersOf) { @@ -254,18 +275,13 @@ template class CallGraphBuilder { template template [[nodiscard]] CallGraph -CallGraph::deserialize(const nlohmann::json &PrecomputedCG, +CallGraph::deserialize(const CallGraphData &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()); + CGBuilder.reserve(PrecomputedCG.FToFunctionVertexTy.size()); - for (const auto &[FunName, CallerIDs] : PrecomputedCG.items()) { + for (const auto &[FunName, CallerIDs] : PrecomputedCG.FToFunctionVertexTy) { const auto &Fun = std::invoke(GetFunctionFromName, FunName); if (!Fun) { PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", @@ -277,11 +293,10 @@ CallGraph::deserialize(const nlohmann::json &PrecomputedCG, CEdges->reserve(CallerIDs.size()); for (const auto &JId : CallerIDs) { - auto Id = JId.get(); - const auto &CS = std::invoke(GetInstructionFromId, Id); + const auto &CS = std::invoke(GetInstructionFromId, JId); if (!CS) { PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", - "Invalid CAll-Instruction Id: " << Id); + "Invalid Call-Instruction Id: " << JId); } CGBuilder.addCallEdge(CS, Fun); diff --git a/include/phasar/ControlFlow/CallGraphData.h b/include/phasar/ControlFlow/CallGraphData.h new file mode 100644 index 0000000000..ed50d66e42 --- /dev/null +++ b/include/phasar/ControlFlow/CallGraphData.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (c) 2024 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_CONTROLFLOW_CALLGRAPHDATA_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_CALLGRAPHDATA_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +struct CallGraphData { + // Mangled FunName --> [CS-IDs] + std::unordered_map> FToFunctionVertexTy{}; + + CallGraphData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static CallGraphData deserializeJson(const llvm::Twine &Path); + static CallGraphData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_CALLGRAPHDATA_H diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index fea37796cf..bbdd5b75f2 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -106,11 +106,23 @@ template class ICFGBase { void print(llvm::raw_ostream &OS = llvm::outs()) const { self().printImpl(OS); } + + /// Prints the underlying call-graph as Json to the given output-stream + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const { + self().printAsJsonImpl(OS); + } + /// Returns the underlying call-graph as JSON - [[nodiscard]] nlohmann::json getAsJson() const { + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const { return self().getAsJsonImpl(); } + [[nodiscard]] size_t getNumCallSites() const noexcept { + return self().getNumCallSitesImpl(); + } + private: const Derived &self() const noexcept { return static_cast(*this); @@ -121,7 +133,7 @@ template class ICFGBase { /// from the given analysis-Domain template // NOLINTNEXTLINE(readability-identifier-naming) -constexpr bool is_icfg_v = is_crtp_base_of_v +PSR_CONCEPT is_icfg_v = is_crtp_base_of_v &&std::is_same_v &&std::is_same_v; diff --git a/include/phasar/ControlFlow/SparseCFGBase.h b/include/phasar/ControlFlow/SparseCFGBase.h new file mode 100644 index 0000000000..3d5f531c26 --- /dev/null +++ b/include/phasar/ControlFlow/SparseCFGBase.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_CONTROLFLOW_SPARSECFGBASE_H +#define PHASAR_CONTROLFLOW_SPARSECFGBASE_H + +#include "phasar/ControlFlow/CFGBase.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Nullable.h" + +namespace psr { +template class SparseCFGBase : public CFGBase { +public: + using typename CFGBase::n_t; + using typename CFGBase::f_t; + + /// Gets the next instruction in control-flow order, starting from + /// FromInstruction, that may use or define Val. + /// If the next user is ambiguous, returns null. + [[nodiscard]] Nullable + nextUserOrNull(ByConstRef FromInstruction) const { + return self().nextUserOrNullImpl(FromInstruction); + } + +protected: + using CFGBase::self; +}; + +template +// NOLINTNEXTLINE(readability-identifier-naming) +constexpr bool is_sparse_cfg_v = is_crtp_base_of_v + &&std::is_same_v + &&std::is_same_v; + +} // namespace psr + +#endif // PHASAR_CONTROLFLOW_SPARSECFGBASE_H diff --git a/include/phasar/ControlFlow/SparseCFGProvider.h b/include/phasar/ControlFlow/SparseCFGProvider.h new file mode 100644 index 0000000000..1ef2882f6a --- /dev/null +++ b/include/phasar/ControlFlow/SparseCFGProvider.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H +#define PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H + +#include "phasar/Utils/ByRef.h" + +#include + +namespace psr { +template T valueOf(T Val) { return Val; } + +template class SparseCFGProvider { +public: + using f_t = F; + using v_t = V; + + template + [[nodiscard]] decltype(auto) getSparseCFG(ByConstRef Fun, + const D &Val) const { + using psr::valueOf; + static_assert(std::is_convertible_v); + return self().getSparseCFGImpl(Fun, valueOf(Val)); + } + +private: + Derived &self() noexcept { return static_cast(*this); } + const Derived &self() const noexcept { + return static_cast(*this); + } +}; + +template +struct has_getSparseCFG : std::false_type {}; // NOLINT +template +struct has_getSparseCFG< + T, D, + std::void_t().getSparseCFG( + std::declval(), std::declval()))>> + : std::true_type {}; + +template +// NOLINTNEXTLINE +static constexpr bool has_getSparseCFG_v = has_getSparseCFG::value; +} // namespace psr + +#endif // PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H diff --git a/include/phasar/DB.h b/include/phasar/DB.h index f77b88f01f..4ddd16a5e0 100644 --- a/include/phasar/DB.h +++ b/include/phasar/DB.h @@ -10,8 +10,12 @@ #ifndef PHASAR_DB_H #define PHASAR_DB_H -#include "phasar/DB/Hexastore.h" +#include "phasar/Config/phasar-config.h" #include "phasar/DB/ProjectIRDBBase.h" + +#ifdef PHASAR_HAS_SQLITE +#include "phasar/DB/Hexastore.h" #include "phasar/DB/Queries.h" +#endif #endif // PHASAR_DB_H diff --git a/include/phasar/DB/Hexastore.h b/include/phasar/DB/Hexastore.h index 02bfb698a6..d6960c903c 100644 --- a/include/phasar/DB/Hexastore.h +++ b/include/phasar/DB/Hexastore.h @@ -10,17 +10,20 @@ #ifndef PHASAR_DB_HEXASTORE_H_ #define PHASAR_DB_HEXASTORE_H_ -#include "phasar/DB/Queries.h" +#include "phasar/Config/phasar-config.h" +#ifndef PHASAR_HAS_SQLITE +#error \ + "Hexastore requires SQLite3. Please install libsqlite3-dev and reconfigure PhASAR." +#endif #include "llvm/Support/raw_ostream.h" -#include "boost/format.hpp" -#include "sqlite3.h" - #include #include #include +struct sqlite3; + namespace psr { /** * @brief Holds the results of a query to the Hexastore. @@ -51,6 +54,7 @@ struct HSResult { LHS.Object == RHS.Object; } }; + /** * A Hexastore is an efficient approach to store large graphs. * This approach is based on the paper "Database-Backed Program Analysis diff --git a/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h b/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h index 2d05cb54c4..e78af20707 100644 --- a/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h +++ b/include/phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h @@ -22,25 +22,25 @@ namespace psr { /// hash_value(const EdgeFunctionTy&). /// /// This cache is *not* thread-safe. -template -class DefaultEdgeFunctionSingletonCache +template +class DefaultEdgeFunctionSingletonCacheImpl : public EdgeFunctionSingletonCache { public: - DefaultEdgeFunctionSingletonCache() noexcept = default; + DefaultEdgeFunctionSingletonCacheImpl() noexcept = default; - DefaultEdgeFunctionSingletonCache(const DefaultEdgeFunctionSingletonCache &) = - delete; - DefaultEdgeFunctionSingletonCache & - operator=(const DefaultEdgeFunctionSingletonCache &) = delete; + DefaultEdgeFunctionSingletonCacheImpl( + const DefaultEdgeFunctionSingletonCacheImpl &) = delete; + DefaultEdgeFunctionSingletonCacheImpl & + operator=(const DefaultEdgeFunctionSingletonCacheImpl &) = delete; - DefaultEdgeFunctionSingletonCache( - DefaultEdgeFunctionSingletonCache &&) noexcept = default; - DefaultEdgeFunctionSingletonCache & - operator=(DefaultEdgeFunctionSingletonCache &&) noexcept = delete; - ~DefaultEdgeFunctionSingletonCache() override = default; + DefaultEdgeFunctionSingletonCacheImpl( + DefaultEdgeFunctionSingletonCacheImpl &&) noexcept = default; + DefaultEdgeFunctionSingletonCacheImpl & + operator=(DefaultEdgeFunctionSingletonCacheImpl &&) noexcept = delete; + ~DefaultEdgeFunctionSingletonCacheImpl() override = default; [[nodiscard]] const void * - lookup(ByConstRef EF) const noexcept override { + lookup(const EdgeFunctionTy &EF) const noexcept override { return Cache.lookup(&EF); } @@ -50,13 +50,10 @@ class DefaultEdgeFunctionSingletonCache assert(Inserted); } - void erase(ByConstRef EF) noexcept override { - Cache.erase(&EF); - } + void erase(const EdgeFunctionTy &EF) noexcept override { Cache.erase(&EF); } template - [[nodiscard]] EdgeFunction - createEdgeFunction(ArgTys &&...Args) { + [[nodiscard]] EdgeFunction createEdgeFunction(ArgTys &&...Args) { return CachedEdgeFunction{ EdgeFunctionTy{std::forward(Args)...}, this}; } @@ -92,19 +89,29 @@ class DefaultEdgeFunctionSingletonCache llvm::DenseMap Cache; }; +template +class DefaultEdgeFunctionSingletonCache + : public DefaultEdgeFunctionSingletonCacheImpl< + EdgeFunctionTy, typename EdgeFunctionTy::l_t> { +public: + using DefaultEdgeFunctionSingletonCacheImpl< + EdgeFunctionTy, + typename EdgeFunctionTy::l_t>::DefaultEdgeFunctionSingletonCacheImpl; +}; + template class DefaultEdgeFunctionSingletonCache< EdgeFunctionTy, std::enable_if_t>> { public: [[nodiscard]] const void * - lookup(ByConstRef /*EF*/) const noexcept override { + lookup(const EdgeFunctionTy & /*EF*/) const noexcept override { return nullptr; } void insert(const EdgeFunctionTy * /*EF*/, const void * /*Mem*/) override { assert(false && "We should never go here"); } - void erase(ByConstRef /*EF*/) noexcept override { + void erase(const EdgeFunctionTy & /*EF*/) noexcept override { assert(false && "We should never go here"); } [[nodiscard]] EdgeFunction diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 7f8e98df88..4a74d9e3ff 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -12,6 +12,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/DenseMapInfo.h" @@ -53,7 +54,7 @@ struct IsEdgeFunction< } // namespace detail template -static inline constexpr bool IsEdgeFunction = detail::IsEdgeFunction::value; +static constexpr bool IsEdgeFunction = detail::IsEdgeFunction::value; #else // clang-format off diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h index 53e652c0fd..8b6c096633 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h @@ -46,7 +46,7 @@ template class EdgeFunctionSingletonCache { /// Checks whether the edge function EF is cached in this cache. Returns the /// cached entry if found, else nullptr. [[nodiscard]] virtual const void * - lookup(ByConstRef EF) const noexcept = 0; + lookup(const EdgeFunctionTy &EF) const noexcept = 0; /// Inserts the cache-entry Mem for the edge function *EF into the cache. /// Typically, EF points into the buffer pointed to by Mem. Both pointers are @@ -57,7 +57,7 @@ template class EdgeFunctionSingletonCache { /// Erases the cache-entry associated with the edge function EF from the /// cache. - virtual void erase(ByConstRef EF) noexcept = 0; + virtual void erase(const EdgeFunctionTy &EF) noexcept = 0; template [[nodiscard]] auto createEdgeFunction(ArgTys &&...Args) { diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h index de9db609a6..bfbc9d2333 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h @@ -132,6 +132,23 @@ defaultComposeOrNull(EdgeFunctionRef This, return nullptr; } +template +EdgeFunction +defaultComposeOrNull(const EdgeFunction &This, + const EdgeFunction &SecondFunction) noexcept { + if (llvm::isa>(SecondFunction)) { + return This; + } + if (SecondFunction.isConstant() || llvm::isa>(This) || + llvm::isa>(This)) { + return SecondFunction; + } + if (llvm::isa>(This)) { + return This; + } + return nullptr; +} + template struct ConstantEdgeFunction { using l_t = L; using JLattice = JoinLatticeTraits; @@ -409,6 +426,26 @@ EdgeFunction defaultJoinOrNull(EdgeFunctionRef This, return nullptr; } +template +EdgeFunction defaultJoinOrNull(const EdgeFunction &This, + const EdgeFunction &OtherFunction) { + if (llvm::isa>(OtherFunction) || llvm::isa>(This)) { + return OtherFunction; + } + if (llvm::isa>(OtherFunction) || OtherFunction == This || + llvm::isa>(This)) { + return This; + } + if (llvm::isa>(OtherFunction)) { + if constexpr (N > 0) { + return JoinEdgeFunction::create(This, OtherFunction); + } else if constexpr (HasJoinLatticeTraits) { + return AllBottom{}; + } + } + return nullptr; +} + template EdgeFunction EdgeIdentity::join(EdgeFunctionRef This, const EdgeFunction &OtherFunction) { diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index 83520ba072..c416571c2f 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -17,6 +17,7 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H #define PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H +#include "phasar/Utils/Macros.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" @@ -83,7 +84,7 @@ template struct IsFlowFunction { /// Helper template to check at compile-time whether a type implements the /// FlowFunction interface, no matter which data-flow fact type it uses. template -static constexpr bool is_flowfunction_v = IsFlowFunction::value; // NOLINT +PSR_CONCEPT is_flowfunction_v = IsFlowFunction::value; // NOLINT /// Given a flow-function type FF, returns a (smart) pointer type pointing to FF template >> @@ -131,7 +132,7 @@ Container makeContainer(Range &&Rng) { Container C; reserveIfPossible(C, Rng.size()); for (auto &&Fact : Rng) { - C.insert(std::forward(Fact)); + C.insert(PSR_FWD(Fact)); } return C; } @@ -499,13 +500,13 @@ template class FlowFunctionTemplates { struct GenManyAndKillAllOthers final : public FlowFunction { GenManyAndKillAllOthers(Container &&GenValues, d_t FromValue) - : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} + : GenValues(std::move(GenValues)), FromValue(FromValue) { + this->GenValues.insert(std::move(FromValue)); + } container_type computeTargets(d_t Source) override { if (Source == FromValue) { - auto Ret = GenValues; - Ret.insert(std::move(Source)); - return Ret; + return GenValues; } return {}; } @@ -929,16 +930,16 @@ class [[deprecated]] Compose : public FlowFunction { using typename FlowFunction::container_type; - Compose(const std::vector> &Funcs) : Funcs(Funcs) {} + Compose(const std::vector &Funcs) : Funcs(Funcs) {} ~Compose() override = default; - container_type computeTargets(const D &Source) override { - container_type Current(Source); - for (const FlowFunctionType &Func : Funcs) { + container_type computeTargets(D Source) override { + container_type Current{Source}; + for (const FlowFunctionPtrType &Func : Funcs) { container_type Next; for (const D &Fact : Current) { - container_type Target = Func.computeTargets(Fact); + container_type Target = Func->computeTargets(Fact); Next.insert(Target.begin(), Target.end()); } Current = Next; @@ -947,11 +948,11 @@ class [[deprecated]] Compose : public FlowFunction { } static FlowFunctionPtrType - compose(const std::vector &Funcs) { - std::vector Vec; - for (const FlowFunctionType &Func : Funcs) { + compose(const std::vector &Funcs) { + std::vector Vec; + for (const FlowFunctionPtrType &Func : Funcs) { if (Func != Identity::getInstance()) { - Vec.insert(Func); + Vec.push_back(Func); } } if (Vec.size() == 1) { // NOLINT(readability-container-size-empty) @@ -964,7 +965,7 @@ class [[deprecated]] Compose : public FlowFunction { } protected: - const std::vector Funcs; + const std::vector Funcs; }; //===----------------------------------------------------------------------===// diff --git a/include/phasar/DataFlow/IfdsIde/GenericFlowFunction.h b/include/phasar/DataFlow/IfdsIde/GenericFlowFunction.h new file mode 100644 index 0000000000..d124e8a013 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/GenericFlowFunction.h @@ -0,0 +1,108 @@ +#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H +#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H + +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" + +namespace psr { +/// Encapsulates an unmanaged pointer to a FlowFunction +template > +class GenericFlowFunctionView { +public: + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = std::unique_ptr; + + using container_type = Container; + using value_type = typename container_type::value_type; + + GenericFlowFunctionView() noexcept = default; + GenericFlowFunctionView(FlowFunctionType *FF) noexcept : FF(FF) {} + + GenericFlowFunctionView(const GenericFlowFunctionView &) noexcept = default; + GenericFlowFunctionView & + operator=(const GenericFlowFunctionView &) noexcept = default; + + ~GenericFlowFunctionView() = default; + + [[nodiscard]] container_type computeTargets(D Source) const { + assert(FF != nullptr); + return FF->computeTargets(std::move(Source)); + } + + explicit operator bool() const noexcept { return FF; } + + [[nodiscard]] bool operator==(GenericFlowFunctionView Other) const noexcept { + return FF == Other.FF; + } + [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { + return FF == nullptr; + } + [[nodiscard]] bool operator!=(GenericFlowFunctionView Other) const noexcept { + return !(*this == Other); + } + [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return FF; } + +private: + FlowFunctionType *FF = nullptr; +}; + +/// Encapsulates a managed pointer to a FlowFunction +template > +class GenericFlowFunction { +public: + using FlowFunctionType = FlowFunction; + using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType; + + using container_type = Container; + using value_type = typename container_type::value_type; + + GenericFlowFunction() noexcept = default; + GenericFlowFunction(FlowFunctionPtrType FF) noexcept : FF(std::move(FF)) {} + template >>> + GenericFlowFunction(T &&FF) + : FF(std::make_unique>(std::forward(FF))) {} + + template + explicit GenericFlowFunction(std::in_place_type_t /*unused*/, + ArgTys &&...Args) + : FF(std::make_unique(std::forward(Args)...)) {} + + GenericFlowFunction(GenericFlowFunction &&) noexcept = default; + GenericFlowFunction &operator=(GenericFlowFunction &&) noexcept = default; + + GenericFlowFunction(const GenericFlowFunction &) = delete; + GenericFlowFunction &operator=(const GenericFlowFunction &) = delete; + + ~GenericFlowFunction() = default; + + [[nodiscard]] container_type computeTargets(D Source) const { + assert(FF != nullptr); + return FF->computeTargets(std::move(Source)); + } + + explicit operator bool() const noexcept { return FF; } + + operator GenericFlowFunctionView() const noexcept { + return FF.get(); + } + + [[nodiscard]] bool + operator==(GenericFlowFunctionView Other) const noexcept { + return FF == Other.FF; + } + [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { + return FF == nullptr; + } + [[nodiscard]] bool + operator!=(GenericFlowFunctionView Other) const noexcept { + return !(*this == Other); + } + [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return FF; } + +private: + FlowFunctionPtrType FF; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H diff --git a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h index 4cdc3610f9..57120a4398 100644 --- a/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h @@ -10,7 +10,6 @@ #ifndef PHASAR_DATAFLOW_IFDSIDE_IDETABULATIONPROBLEM_H_ #define PHASAR_DATAFLOW_IFDSIDE_IDETABULATIONPROBLEM_H_ -#include "phasar/ControlFlow/ICFGBase.h" #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" @@ -18,17 +17,14 @@ #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" #include "phasar/DataFlow/IfdsIde/InitialSeeds.h" +#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.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/SemiRing.h" #include "phasar/Utils/Soundness.h" -#include "llvm/ADT/StringRef.h" - #include -#include -#include #include #include #include @@ -62,6 +58,7 @@ template , public EdgeFunctions, public JoinLattice, + public SemiRing, public AllTopFnProvider { public: using ProblemAnalysisDomain = AnalysisDomainTy; @@ -132,7 +129,7 @@ class IDETabulationProblem : public FlowFunctions, /// Generates a text report of the results that is written to the specified /// output stream. virtual void - emitTextReport([[maybe_unused]] const SolverResults &Results, + emitTextReport([[maybe_unused]] GenericSolverResults Results, llvm::raw_ostream &OS = llvm::outs()) { OS << "No text report available!\n"; } @@ -140,7 +137,7 @@ class IDETabulationProblem : public FlowFunctions, /// Generates a graphical report, e.g. in html or other markup languages, of /// the results that is written to the specified output stream. virtual void emitGraphicalReport( - [[maybe_unused]] const SolverResults &Results, + [[maybe_unused]] GenericSolverResults Results, llvm::raw_ostream &OS = llvm::outs()) { OS << "No graphical report available!\n"; } @@ -149,6 +146,8 @@ class IDETabulationProblem : public FlowFunctions, /// the level of soundness is ignored. Otherwise, true. virtual bool setSoundness(Soundness /*S*/) { return false; } + const ProjectIRDBBase *getProjectIRDB() const noexcept { return IRDB; } + protected: typename FlowFunctions::FlowFunctionPtrType generateFromZero(d_t FactToGenerate) { diff --git a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h index e669a26a9d..cbafa68c3a 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h @@ -13,7 +13,6 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/Domain/AnalysisDomain.h" -#include "phasar/Domain/BinaryDomain.h" #include #include diff --git a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h index 60e5ae23e3..db7ea654e7 100644 --- a/include/phasar/DataFlow/IfdsIde/InitialSeeds.h +++ b/include/phasar/DataFlow/IfdsIde/InitialSeeds.h @@ -11,6 +11,7 @@ #define PHASAR_DATAFLOW_IFDSIDE_INITIALSEEDS_H #include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/Printer.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/Support/Compiler.h" @@ -75,36 +76,13 @@ template class InitialSeeds { [[nodiscard]] GeneralizedSeeds getSeeds() && { return std::move(Seeds); } void dump(llvm::raw_ostream &OS = llvm::errs()) const { - - auto printNode = [&](auto &&Node) { // NOLINT - if constexpr (std::is_pointer_v && - is_llvm_printable_v>) { - OS << *Node; - } else { - OS << Node; - } - }; - - auto printFact = [&](auto &&Node) { // NOLINT - if constexpr (std::is_pointer_v && - is_llvm_printable_v>) { - OS << *Node; - } else { - OS << Node; - } - }; - OS << "======================== Initial Seeds ========================\n"; for (const auto &[Node, Facts] : Seeds) { - OS << "At "; - printNode(Node); - OS << "\n"; + OS << "At " << NToString(Node) << '\n'; for (const auto &[Fact, Value] : Facts) { - OS << "> "; - printFact(Fact); - OS << " --> \\." << Value << "\n"; + OS << "> " << DToString(Fact) << " --> \\." << LToString(Value) << '\n'; } - OS << "\n"; + OS << '\n'; } OS << "========================== End Seeds ==========================\n"; } diff --git a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h new file mode 100644 index 0000000000..7d357a0a05 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h @@ -0,0 +1,191 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H + +#include "phasar/DB/ProjectIRDBBase.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include +#include +#include +#include + +namespace psr { +template class Compressor; + +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + FromInt.reserve(Capacity); + } + + uint32_t getOrInsert(T Elem) { + auto [It, Inserted] = ToInt.try_emplace(Elem, ToInt.size()); + if (Inserted) { + FromInt.push_back(Elem); + } + return It->second; + } + + std::optional getOrNull(T Elem) const { + if (auto It = ToInt.find(Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + T operator[](size_t Idx) const noexcept { + assert(Idx < FromInt.size()); + return FromInt[Idx]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.capacity() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + +private: + llvm::DenseMap ToInt; + llvm::SmallVector FromInt; +}; + +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + } + + uint32_t getOrInsert(const T &Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = FromInt.size(); + auto *Ins = &FromInt.emplace_back(Elem); + ToInt[Ins] = Ret; + return Ret; + } + + uint32_t getOrInsert(T &&Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = FromInt.size(); + auto *Ins = &FromInt.emplace_back(std::move(Elem)); + ToInt[Ins] = Ret; + return Ret; + } + + std::optional getOrNull(const T &Elem) const { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + const T &operator[](size_t Idx) const noexcept { + assert(Idx < FromInt.size()); + return FromInt[Idx]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.size() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + +private: + struct DSI : llvm::DenseMapInfo { + static auto getHashValue(const T *Elem) noexcept { + assert(Elem != nullptr); + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::getHashValue(*Elem); + } else { + return std::hash{}(*Elem); + } + } + static auto isEqual(const T *LHS, const T *RHS) noexcept { + if (LHS == RHS) { + return true; + } + if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() || + RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) { + return false; + } + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::isEqual(*LHS, *RHS); + } else { + return *LHS == *RHS; + } + } + }; + + std::deque FromInt; + llvm::DenseMap ToInt; +}; + +struct NoneCompressor final { + constexpr NoneCompressor() noexcept = default; + + template >> + constexpr NoneCompressor(const T & /*unused*/) noexcept {} + + template + [[nodiscard]] decltype(auto) getOrInsert(T &&Val) const noexcept { + return std::forward(Val); + } + template + [[nodiscard]] decltype(auto) operator[](T &&Val) const noexcept { + return std::forward(Val); + } + void reserve(size_t /*unused*/) const noexcept {} + + [[nodiscard]] size_t size() const noexcept { return 0; } + [[nodiscard]] size_t capacity() const noexcept { return 0; } +}; + +class LLVMProjectIRDB; + +/// Once we have fast instruction IDs (as we already have in IntelliSecPhasar), +/// we might want to create a specialization for T/const llvm::Value * that uses +/// the IDs from the IRDB +template struct NodeCompressorTraits { + using type = Compressor; + + static type create(const ProjectIRDBBase + * /*IRDB*/) noexcept(noexcept(type())) { + return type(); + } +}; + +template struct ValCompressorTraits { + using type = Compressor; + using id_type = uint32_t; +}; + +template +struct ValCompressorTraits>> { + using type = NoneCompressor; + using id_type = T; +}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h b/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h index 94b98ed893..9beaa0d63d 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h @@ -1,3 +1,6 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H + /****************************************************************************** * Copyright (c) 2022 Philipp Schubert. * All rights reserved. This program and the accompanying materials are made @@ -7,9 +10,6 @@ * 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 }; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h new file mode 100644 index 0000000000..dbcd46561a --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h @@ -0,0 +1,163 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_EDGEFUNCTIONCACHE_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_EDGEFUNCTIONCACHE_H + +#include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" +#include "phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCacheStats.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/EquivalenceClassMap.h" +#include "phasar/Utils/StableVector.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { + +template +class EdgeFunctionCache : private EdgeFunctionCacheStats { + using domain_t = typename ProblemTy::ProblemAnalysisDomain; + using d_t = typename domain_t::d_t; + using n_t = typename domain_t::n_t; + using f_t = typename domain_t::f_t; + using l_t = typename domain_t::l_t; + +public: + EdgeFunctionCache() noexcept = default; + + using EdgeFunctionPtrType = EdgeFunction; + + EdgeFunctionPtrType + getNormalEdgeFunction(ProblemTy &Problem, ByConstRef Curr, + ByConstRef CurrNode, ByConstRef Succ, + ByConstRef SuccNode, uint64_t CurrSuccId, + uint64_t CurrNodeSuccNodeId) { + auto &EqMap = NormalAndCtrEFCache[CurrSuccId]; + if (!EqMap) { + EqMap = &NormalMapOwner.emplace_back(); + } + return EqMap->getOrInsertLazy(CurrNodeSuccNodeId, [&] { + ++NormalAndCtrEFCacheCumulSize; + return Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); + }); + } + + EdgeFunctionPtrType + getCallToRetEdgeFunction(ProblemTy &Problem, ByConstRef CallSite, + ByConstRef CallNode, ByConstRef RetSite, + ByConstRef RetSiteNode, + llvm::ArrayRef Callees, uint64_t CallRetId, + uint64_t CallNodeRetNodeId) { + auto &EqMap = NormalAndCtrEFCache[CallRetId]; + if (!EqMap) { + EqMap = &NormalMapOwner.emplace_back(); + } + return EqMap->getOrInsertLazy(CallNodeRetNodeId, [&] { + ++NormalAndCtrEFCacheCumulSize; + return Problem.getCallToRetEdgeFunction(CallSite, CallNode, RetSite, + RetSiteNode, Callees); + }); + } + + EdgeFunctionPtrType + getCallEdgeFunction(ProblemTy &Problem, ByConstRef CallSite, + ByConstRef SrcNode, ByConstRef Callee, + ByConstRef DestNode, uint64_t CSCalleeId, + uint64_t SrcDestNodeId) { + auto &EqMap = CallEFCache[CSCalleeId]; + if (!EqMap) { + EqMap = &NormalMapOwner.emplace_back(); + } + return EqMap->getOrInsertLazy(SrcDestNodeId, [&] { + ++CallEFCacheCumulSize; + return Problem.getCallEdgeFunction(CallSite, SrcNode, Callee, DestNode); + }); + } + + EdgeFunctionPtrType + getReturnEdgeFunction(ProblemTy &Problem, ByConstRef CallSite, + ByConstRef Callee, ByConstRef ExitInst, + ByConstRef ExitNode, ByConstRef RetSite, + ByConstRef RetSiteNode, uint32_t ExitId, + uint64_t CSRSId, uint64_t ExitRSNodeId) { + auto &EqMap = RetEFCache[ExitId]; + if (!EqMap) { + EqMap = &RetMapOwner.emplace_back(); + } + return EqMap->getOrInsertLazy(std::make_pair(CSRSId, ExitRSNodeId), [&] { + ++RetEFCacheCumulSize; + return Problem.getReturnEdgeFunction(CallSite, Callee, ExitInst, ExitNode, + RetSite, RetSiteNode); + }); + } + + EdgeFunctionPtrType + getSummaryEdgeFunction(ProblemTy &Problem, ByConstRef CallSite, + ByConstRef CallNode, ByConstRef RetSite, + ByConstRef RetSiteNode, uint64_t CSRSId, + uint64_t CSNodeRSNodeId) { + auto &EqMap = SummaryEFCache[CSRSId]; + if (!EqMap) { + EqMap = &NormalMapOwner.emplace_back(); + } + return EqMap->getOrInsertLazy(CSNodeRSNodeId, [&] { + ++SummaryEFCacheCumulSize; + return Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, + RetSiteNode); + }); + } + + void clear() { + NormalAndCtrEFCache.clear(); + CallEFCache.clear(); + RetEFCache.clear(); + SummaryEFCache.clear(); + NormalMapOwner.clear(); + RetMapOwner.clear(); + } + + void dumpStats(llvm::raw_ostream &OS) const { OS << getStats(); } + + [[nodiscard]] EdgeFunctionCacheStats getStats() const noexcept { + size_t Sz = 0; + size_t MaxInnerSz = 0; + llvm::SmallVector Sizes; + for (const auto &[Edge, NormalAndCtrEF] : NormalAndCtrEFCache) { + Sz += NormalAndCtrEF->size(); + MaxInnerSz = std::max(MaxInnerSz, NormalAndCtrEF->size()); + Sizes.push_back(NormalAndCtrEF->size()); + } + + std::sort(Sizes.begin(), Sizes.end()); + + EdgeFunctionCacheStats Ret = *this; + Ret.NormalAndCtrEFCacheCumulSizeReduced = Sz; + Ret.AvgEFPerEdge = double(Sz) / NormalAndCtrEFCache.size(); + Ret.MaxEFPerEdge = MaxInnerSz; + Ret.MedianEFPerEdge = Sizes.empty() ? 0 : Sizes[Sizes.size() / 2]; + return Ret; + } + +private: + StableVector> + NormalMapOwner{}; + StableVector< + EquivalenceClassMapNG, EdgeFunctionPtrType>> + RetMapOwner{}; + /// NOTE: For CTR, the Callees set is implied by the CallSite/RetSite + llvm::DenseMap *> + NormalAndCtrEFCache{}; + llvm::DenseMap *> + CallEFCache{}; + llvm::DenseMap, + EdgeFunctionPtrType> *> + RetEFCache{}; + llvm::DenseMap *> + SummaryEFCache{}; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_EDGEFUNCTIONCACHE_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCacheStats.h b/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCacheStats.h new file mode 100644 index 0000000000..a919504604 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCacheStats.h @@ -0,0 +1,27 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_EDGEFUNCTIONCACHESTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_EDGEFUNCTIONCACHESTATS_H + +#include + +namespace llvm { +class raw_ostream; +} // namespace llvm + +namespace psr { + +struct EdgeFunctionCacheStats { + size_t NormalAndCtrEFCacheCumulSize = 0; + size_t NormalAndCtrEFCacheCumulSizeReduced = 0; + double AvgEFPerEdge = 0; + size_t MedianEFPerEdge = 0; + size_t MaxEFPerEdge = 0; + size_t CallEFCacheCumulSize = 0; + size_t RetEFCacheCumulSize = 0; + size_t SummaryEFCacheCumulSize = 0; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const EdgeFunctionCacheStats &S); +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_EDGEFUNCTIONCACHESTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h new file mode 100644 index 0000000000..1c5b77c21b --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h @@ -0,0 +1,45 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHENG_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHENG_H + +#include "phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheStats.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h" + +#include "llvm/Support/raw_ostream.h" + +namespace psr { + +template +class FlowEdgeFunctionCacheNG + : public FlowFunctionCache, + public EdgeFunctionCache { +public: + explicit FlowEdgeFunctionCacheNG(ProblemTy & /*Problem*/) + : FlowFunctionCache(), + EdgeFunctionCache() {} + + void clearFlowFunctions() { + this->FlowFunctionCache::clear(); + } + + void clear() { + this->FlowFunctionCache::clear(); + this->EdgeFunctionCache::clear(); + } + + void reserve(size_t NumInsts, size_t NumCalls, size_t NumFuns) { + this->FlowFunctionCache::reserve(NumInsts, NumCalls, + NumFuns); + /// XXX: reserve edge functions as well, once possible! + } + + [[nodiscard]] FlowEdgeFunctionCacheStats getStats() const noexcept { + return {this->FlowFunctionCache::getStats(), + this->EdgeFunctionCache::getStats()}; + } + + void dumpStats(llvm::raw_ostream &OS) const { OS << getStats(); } +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHENG_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheStats.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheStats.h new file mode 100644 index 0000000000..b88aa4679f --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheStats.h @@ -0,0 +1,15 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHESTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHESTATS_H + +#include "phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCacheStats.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h" + +namespace psr { +struct FlowEdgeFunctionCacheStats : FlowFunctionCacheStats, + EdgeFunctionCacheStats { + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const FlowEdgeFunctionCacheStats &Stats); +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHESTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h new file mode 100644 index 0000000000..a0f42d821f --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h @@ -0,0 +1,338 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHE_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHE_H + +#include "phasar/DataFlow/IfdsIde/GenericFlowFunction.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/PointerUtils.h" +#include "phasar/Utils/TableWrappers.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +namespace detail { +template +constexpr inline bool IsFlowFunction = false; + +template +constexpr inline bool + IsFlowFunction().computeTargets( + std::declval()))>> = true; + +template +constexpr inline bool IsFlowFunctionPtr = false; + +template +constexpr inline bool + IsFlowFunctionPtr()->computeTargets( + std::declval()))>> = true; + +template struct AutoAddZeroFF { + FFTy FF; + D ZeroValue; + + auto computeTargets(ByConstRef Source) { + auto Ret = FF.computeTargets(Source); + if (Source == ZeroValue) { + Ret.insert(ZeroValue); + } + return Ret; + } + + [[nodiscard]] bool operator==(std::nullptr_t) const noexcept { + if constexpr (std::is_same_v> || + std::is_same_v>) { + return FF == nullptr; + } else { + return std::is_same_v, std::nullptr_t>; + } + } + [[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { + return !(*this == nullptr); + } +}; + +template +AutoAddZeroFF(FFTy, D) -> AutoAddZeroFF, std::decay_t>; + +template +struct IntraFlowFunctionsMixin { + DenseTable1d NormalFFCache{}; + /// NOTE: The Callees set is completely implied by CallSite/RetSite and does + /// not need to be stored! Note2: Assume here that the callees set is + /// invariant during the IFDS/IDE analysis + DenseTable1d CtrFFCache{}; + + void reserveIntraFFs(size_t NumInsts, size_t NumCalls) { + NormalFFCache.reserve(NumInsts); + CtrFFCache.reserve(NumCalls); + } +}; + +template +struct IntraFlowFunctionsMixin { + /// Whenever possible, use only one map for normal-flows and ctr-flows. The + /// key-spaces are strictly disjoint and paying 75% MaxLoadFactor once less + /// should be good for memory usage + + DenseTable1d NormalFFCache{}; + DenseTable1d &CtrFFCache = NormalFFCache; + + void reserveIntraFFs(size_t NumInsts, size_t /*NumCalls*/) { + NormalFFCache.reserve(NumInsts); + } +}; + +template struct FlowFunctionCacheBase { + using domain_t = typename ProblemTy::ProblemAnalysisDomain; + using d_t = typename domain_t::d_t; + using n_t = typename domain_t::n_t; + using f_t = typename domain_t::f_t; + using normal_ff_t = + std::decay_t().getNormalFlowFunction( + std::declval(), std::declval()))>; + using call_ff_t = + std::decay_t().getCallFlowFunction( + std::declval(), std::declval()))>; + using ret_ff_t = + std::decay_t().getRetFlowFunction( + std::declval(), std::declval(), std::declval(), + std::declval()))>; + using ctr_ff_t = + std::decay_t().getCallToRetFlowFunction( + std::declval(), std::declval(), + std::declval>()))>; + using summary_ff_t = + std::decay_t().getSummaryFlowFunction( + std::declval(), std::declval()))>; + + static_assert(detail::IsFlowFunction || + detail::IsFlowFunctionPtr); + static_assert(detail::IsFlowFunction || + detail::IsFlowFunctionPtr); + static_assert(detail::IsFlowFunction || + detail::IsFlowFunctionPtr); + static_assert(detail::IsFlowFunction || + detail::IsFlowFunctionPtr); + static_assert(detail::IsFlowFunction || + detail::IsFlowFunctionPtr || + std::is_same_v); + + template + static constexpr bool needs_cache_v = + std::is_same_v>, T> || + std::is_same_v, T> || + (detail::IsFlowFunctionPtr && !std::is_pointer_v); +}; + +} // namespace detail + +template +class FlowFunctionCache + : detail::FlowFunctionCacheBase, + detail::IntraFlowFunctionsMixin< + typename detail::FlowFunctionCacheBase::normal_ff_t, + typename detail::FlowFunctionCacheBase::ctr_ff_t> { + using base_t = detail::FlowFunctionCacheBase; + + using typename base_t::call_ff_t; + using typename base_t::ctr_ff_t; + using typename base_t::d_t; + using typename base_t::domain_t; + using typename base_t::f_t; + using typename base_t::n_t; + using typename base_t::normal_ff_t; + using typename base_t::ret_ff_t; + using typename base_t::summary_ff_t; + + using detail::IntraFlowFunctionsMixin::NormalFFCache; + using detail::IntraFlowFunctionsMixin::CtrFFCache; + + template + static constexpr bool needs_cache_v = base_t::template needs_cache_v; + + template + static auto wrapFlowFunction(ProblemTy &Problem, FFTy &&FF) { + using ff_t = std::decay_t; + + if constexpr (AutoAddZero) { + if constexpr (detail::IsFlowFunctionPtr) { + if constexpr (needs_cache_v) { + return detail::AutoAddZeroFF{ + GenericFlowFunctionView(getPointerFrom(FF)), + Problem.getZeroValue()}; + } else { + return detail::AutoAddZeroFF{ + GenericFlowFunction(std::forward(FF)), + Problem.getZeroValue()}; + } + } else { + return detail::AutoAddZeroFF{std::forward(FF), + Problem.getZeroValue()}; + } + } else { + if constexpr (detail::IsFlowFunctionPtr) { + if constexpr (needs_cache_v) { + return GenericFlowFunctionView(getPointerFrom(FF)); + } else { + return GenericFlowFunction(std::forward(FF)); + } + } else { + return std::forward(FF); + } + } + } + + template + decltype(auto) getFlowFunction(CacheTy &Cache, uint64_t Id, + ProblemTy &Problem, FFFactory Fact) { + if constexpr (needs_cache_v) { + auto &Ret = Cache.getOrCreate(Id); + if (!Ret) { + Ret = std::invoke(Fact, Problem); + } + + return wrapFlowFunction(Problem, Ret); + } else { + return wrapFlowFunction(Problem, std::invoke(Fact, Problem)); + } + } + +public: + FlowFunctionCache() noexcept = default; + + decltype(auto) getNormalFlowFunction(ProblemTy &Problem, ByConstRef Curr, + ByConstRef Succ, + uint64_t CurrSuccId) { + return getFlowFunction( + NormalFFCache, CurrSuccId, Problem, [Curr, Succ](ProblemTy &Problem) { + return Problem.getNormalFlowFunction(Curr, Succ); + }); + } + + decltype(auto) getCallToRetFlowFunction(ProblemTy &Problem, + ByConstRef CallSite, + ByConstRef RetSite, + llvm::ArrayRef Callees, + uint64_t CallRetId) { + return getFlowFunction( + CtrFFCache, CallRetId, Problem, + [CallSite, RetSite, Callees](ProblemTy &Problem) { + return Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees); + }); + } + + decltype(auto) getCallFlowFunction(ProblemTy &Problem, + ByConstRef CallSite, + ByConstRef Callee, + uint64_t CSCalleeId) { + return getFlowFunction(CallFFCache, CSCalleeId, Problem, + [CallSite, Callee](ProblemTy &Problem) { + return Problem.getCallFlowFunction( + CallSite, Callee); + }); + } + + /// CAUTION: Keep the ID definitions in mind! + decltype(auto) getRetFlowFunction(ProblemTy &Problem, + ByConstRef CallSite, + ByConstRef Callee, + ByConstRef ExitInst, + ByConstRef RetSite, uint64_t CSExitId, + uint64_t CalleeRSId) { + if constexpr (needs_cache_v) { + if constexpr (std::is_base_of_v< + llvm::Instruction, + std::remove_cv_t>>) { + if (llvm::isa(CallSite)) { + auto &Ret = SimpleRetFFCache.getOrCreate(CSExitId); + if (!Ret) { + Ret = + Problem.getRetFlowFunction(CallSite, Callee, ExitInst, RetSite); + } + return wrapFlowFunction(Problem, Ret); + } + } + + auto &Ret = RetFFCache.getOrCreate(std::make_pair(CSExitId, CalleeRSId)); + if (!Ret) { + Ret = Problem.getRetFlowFunction(CallSite, Callee, ExitInst, RetSite); + } + return wrapFlowFunction(Problem, Ret); + } else { + return wrapFlowFunction( + Problem, + Problem.getRetFlowFunction(CallSite, Callee, ExitInst, RetSite)); + } + } + + decltype(auto) getSummaryFlowFunction(ProblemTy &Problem, + ByConstRef CallSite, + ByConstRef Callee, + uint64_t /*CSCalleeId*/) { + if constexpr (needs_cache_v) { + return GenericFlowFunction( + Problem.getSummaryFlowFunction(CallSite, Callee)); + } else { + return Problem.getSummaryFlowFunction(CallSite, Callee); + } + + /// XXX: The old FECache doesn't cache summary FFs, so refrain from doing + /// this here as well. Enable it, once the user-problems reliably return + /// std::nullptr_t such that caching can be disabled automatically + } + + void clear() noexcept { + NormalFFCache.clear(); + CallFFCache.clear(); + RetFFCache.clear(); + SimpleRetFFCache.clear(); + CtrFFCache.clear(); + SummaryFFCache.clear(); + } + + void reserve(size_t NumInsts, size_t NumCalls, size_t NumFuns) { + detail::IntraFlowFunctionsMixin::reserveIntraFFs( + NumInsts, NumCalls); + CallFFCache.reserve(NumCalls); + RetFFCache.reserve(NumFuns); + SimpleRetFFCache.reserve(NumFuns); + } + + void dumpStats(llvm::raw_ostream &OS) const { OS << getStats(); } + + [[nodiscard]] FlowFunctionCacheStats getStats() const noexcept { + /// TODO (#734): With C++20 use designated aggregate initializers here! + return FlowFunctionCacheStats{ + NormalFFCache.size(), CallFFCache.size(), RetFFCache.size(), + SimpleRetFFCache.size(), CtrFFCache.size(), SummaryFFCache.size(), + }; + } + +private: + DenseTable1d CallFFCache; + /// NOTE: Unfortunately, RetSite cannot imply CallSite (c.f. invoke + /// instructions). Although ExitInst implies CalleeFun, it does not help to + /// store a pair --> padding + /// + /// Keys are (CS, ExitInst, Callee, RetSite) + DenseTable1d, ctr_ff_t> RetFFCache; + /// Here, we restrict ourselves to simple calls: + /// Keys are (CS, ExitInst) + DenseTable1d SimpleRetFFCache; + + DenseTable1d SummaryFFCache; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_FLOWFUNCTIONCACHE_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h new file mode 100644 index 0000000000..049af06a0a --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCacheStats.h @@ -0,0 +1,24 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHESTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWFUNCTIONCACHESTATS_H + +#include + +namespace llvm { +class raw_ostream; +} // namespace llvm + +namespace psr { +struct FlowFunctionCacheStats { + size_t NormalFFCacheSize{}; + size_t CallFFCacheSize{}; + size_t ReturnFFCacheSize{}; + size_t SimpleRetFFCacheSize{}; + size_t CallToRetFFCacheSize{}; + size_t SummaryFFCacheSize{}; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const FlowFunctionCacheStats &S); +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_FLOWFUNCTIONCACHESTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h new file mode 100644 index 0000000000..744c8e7e38 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h @@ -0,0 +1,249 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_GENERICSOLVERRESULTS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_GENERICSOLVERRESULTS_H + +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/ByRef.h" + +#include "llvm/ADT/STLFunctionalExtras.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Value.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include +#include + +namespace psr { + +/// XXX (#734): When upgrading to C++20, create a concept checking valid +/// SolverResults types + +/// A type-erased version of the main functionality of SolverResults. +/// Can be accepted by consumers that don't need deep access to the internals +/// (so, the usual ones). As we have now two kinds of solver-results +/// (SolverResults and IdBasedSolverResults), we need a common way of accessing +/// them. +template class GenericSolverResults final { +public: + using n_t = N; + using d_t = D; + using l_t = L; + + /// NOTE: Don't provide an operator= that constructs a GenericSolverResults + /// from a concrete SolverResults object to prevent issues with object + /// lifetime. Probably that can be mitigated with use of std::launder, but for + /// now, we don't need this complexity + + template >>> + GenericSolverResults(const T &SR) noexcept : VT(&VtableFor>) { + using type = std::decay_t; + + static_assert(std::is_trivially_destructible_v); + static_assert(std::is_trivially_copyable_v, + "SolverResults should only be a *view* into the results of " + "an IFDS/IDE solver."); + + /// NOTE: We reinterpret the bytes of the `Buffer` as `type`, so it better + /// be properly aligned + static_assert( + alignof(type) <= alignof(void *), + "The solver-results type is improperly aligned for the inline buffer"); + static_assert( + sizeof(type) <= sizeof(Buffer), + "The inline buffer is to small to hold this type of solver-result"); + + static_assert(std::is_trivially_copyable_v); + + new (Buffer.data()) type(SR); + } + + [[nodiscard]] l_t resultAt(ByConstRef Stmt, ByConstRef Node) const { + assert(VT != nullptr); + return VT->ResultAt(Buffer.data(), Stmt, Node); + } + + [[nodiscard]] std::unordered_map + resultsAt(ByConstRef Stmt, bool StripZero = false) const { + assert(VT != nullptr); + return VT->ResultsAt(Buffer.data(), Stmt, StripZero); + } + + template >> + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { + assert(VT != nullptr); + assert(VT->IfdsResultsAt != nullptr); + return VT->IfdsResultsAt(Buffer.data(), Stmt); + } + + [[nodiscard]] size_t size() const noexcept { + assert(VT != nullptr); + return VT->Size(Buffer.data()); + } + + [[nodiscard]] bool containsNode(ByConstRef Stmt) const { + return VT->ContainsNode(Buffer.data(), Stmt); + } + + template >> + [[nodiscard]] l_t resultAtInLLVMSSA(const llvm::Instruction *Stmt, + ByConstRef Node) const { + if (const auto *Next = Stmt->getNextNode()) { + return resultAt(Next, Node); + } + return resultAt(Stmt, Node); + } + + template >> + [[nodiscard]] std::unordered_map + resultsAtInLLVMSSA(const llvm::Instruction *Stmt, + bool StripZero = false) const { + if (const auto *Next = Stmt->getNextNode()) { + return resultsAt(Next, StripZero); + } + return resultsAt(Stmt, StripZero); + } + + void foreachResultEntry( + llvm::function_ref)> Handler) const { + assert(VT != nullptr); + VT->ForeachResultEntry(Buffer.data(), Handler); + } + +private: + struct VTableTy { + l_t (*ResultAt)(const void *, ByConstRef, ByConstRef); + std::unordered_map (*ResultsAt)(const void *, ByConstRef, + bool); + std::set (*IfdsResultsAt)(const void *, ByConstRef); + size_t (*Size)(const void *) noexcept; + bool (*ContainsNode)(const void *, ByConstRef); + void (*ForeachResultEntry)( + const void *, llvm::function_ref)>); + }; + + template + static constexpr VTableTy VtableFor{ + [](const void *SR, ByConstRef Stmt, ByConstRef Node) -> l_t { + return static_cast(SR)->resultAt(Stmt, Node); + }, + [](const void *SR, ByConstRef Stmt, + bool StripZero) -> std::unordered_map { + return static_cast(SR)->resultsAt(Stmt, StripZero); + }, + [] { + std::set (*IfdsResultsAt)(const void *, ByConstRef) = nullptr; + if constexpr (std::is_same_v) { + IfdsResultsAt = [](const void *SR, ByConstRef Stmt) { + return static_cast(SR)->ifdsResultsAt(Stmt); + }; + } + return IfdsResultsAt; + }(), + [](const void *SR) noexcept -> size_t { + return static_cast(SR)->size(); + }, + [](const void *SR, ByConstRef Stmt) -> bool { + return static_cast(SR)->containsNode(Stmt); + }, + [](const void *SR, + llvm::function_ref)> Handler) { + static_cast(SR)->foreachResultEntry(Handler); + }}; + + const VTableTy *VT{}; + alignas(alignof(void *)) std::array Buffer{}; +}; + +template +bool ifdsEqual(const SR1 &LHS, const SR2 &RHS) { + if (LHS.size() != RHS.size()) { + return false; + } + + for (const auto &[Row, ColVal] : LHS.rowMapView()) { + if (!RHS.containsNode(Row)) { + return false; + } + + const auto &OtherColVal = LHS.row(Row); + if (ColVal.size() != OtherColVal.size()) { + return false; + } + + for (const auto &[Col, Val] : ColVal) { + if (!OtherColVal.count(Col)) { + return false; + } + } + } + return true; +} + +template +bool checkSREquality(const SR1 &LHS, const SR2 &RHS) { + bool HasError = false; + if (LHS.size() != RHS.size()) { + HasError = true; + llvm::errs() << "The results sizes do not match: " << LHS.size() << " vs " + << RHS.size() << '\n'; + } + + auto ToString = [](const auto &Fact) { + if constexpr (std::is_pointer_v> && + std::is_base_of_v>>>) { + return llvmIRToString(Fact); + } else { + std::string S; + llvm::raw_string_ostream ROS(S); + ROS << Fact; + ROS.flush(); + return S; + } + }; + + for (const auto &[Row, ColVal] : LHS.rowMapView()) { + if (!RHS.containsNode(Row)) { + HasError = true; + llvm::errs() << "RHS does not contain row for Inst: " << ToString(Row) + << '\n'; + } + + auto RHSColVal = RHS.row(Row); + + if (ColVal.size() != RHSColVal.size()) { + HasError = true; + llvm::errs() << "The number of facts at " << ToString(Row) + << " differ: " << ColVal.size() << " vs " << RHSColVal.size() + << '\n'; + } + + for (const auto &[Col, Val] : ColVal) { + auto It = RHSColVal.find(Col); + + if (It == RHSColVal.end()) { + HasError = true; + llvm::errs() << "RHS does not contain fact " << ToString(Col) + << " at Inst " << ToString(Row) << '\n'; + } else if (Val != It->second) { + HasError = true; + llvm::errs() << "The edge values at inst " << ToString(Row) + << " and fact " << ToString(Col) + << " differ: " << ToString(Val) << " vs " + << ToString(It->second) << '\n'; + } + } + } + return !HasError; +} +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_GENERICSOLVERRESULTS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 3274ced1a8..534357f5bb 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -18,6 +18,7 @@ #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDESOLVER_H #include "phasar/Config/Configuration.h" +#include "phasar/ControlFlow/SparseCFGProvider.h" #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionStats.h" @@ -35,15 +36,19 @@ #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Domain/AnalysisDomain.h" #include "phasar/Utils/Average.h" +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/DOTGraph.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/Nullable.h" #include "phasar/Utils/PAMMMacros.h" #include "phasar/Utils/Table.h" #include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/TypeName.h" #include "llvm/Support/raw_ostream.h" #include "nlohmann/json.hpp" @@ -81,16 +86,30 @@ class IDESolver using t_t = typename AnalysisDomainTy::t_t; using v_t = typename AnalysisDomainTy::v_t; + template IDESolver(IDETabulationProblem &Problem, - const i_t *ICF) - : IDEProblem(Problem), ZeroValue(Problem.getZeroValue()), ICF(ICF), + const I *ICF) + : IDEProblem(Problem), ZeroValue(Problem.getZeroValue()), + ICF(&static_cast(*ICF)), SVFG(ICF), SolverConfig(Problem.getIFDSIDESolverConfig()), CachedFlowEdgeFunctions(Problem), AllTop(Problem.allTopFunction()), JumpFn(std::make_shared>()), Seeds(Problem.initialSeeds()) { assert(ICF != nullptr); + + if constexpr (has_getSparseCFG_v) { + NextUserOrNullCB = [](const void *SVFG, ByConstRef Fun, + ByConstRef d3, ByConstRef n) { + auto &&SCFG = static_cast(SVFG)->getSparseCFG(Fun, d3); + return SCFG.nextUserOrNull(n); + }; + } } + IDESolver(IDETabulationProblem *Problem, + const i_t *ICF) + : IDESolver(assertNotNull(Problem), ICF) {} + IDESolver(const IDESolver &) = delete; IDESolver &operator=(const IDESolver &) = delete; IDESolver(IDESolver &&) = delete; @@ -117,7 +136,7 @@ class IDESolver llvm::StringRef(NToString(Cells[I].getRowKey())).trim().str(); std::string NodeStr = - ICF->getFunctionName(ICF->getFunctionOf(Curr)) + "::" + NStr; + ICF->getFunctionName(ICF->getFunctionOf(Curr)).str() + "::" + NStr; J[DataFlowID][NodeStr]; std::string FactStr = llvm::StringRef(DToString(Cells[I].getColumnKey())).trim().str(); @@ -335,6 +354,15 @@ class IDESolver } protected: + Nullable getNextUserOrNull(ByConstRef Fun, ByConstRef d3, + ByConstRef n) { + if (!NextUserOrNullCB || IDEProblem.isZeroValue(d3)) { + return {}; + } + + return NextUserOrNullCB(SVFG, Fun, d3, n); + } + /// Lines 13-20 of the algorithm; processing a call site in the caller's /// context. /// @@ -378,6 +406,15 @@ class IDESolver bool HasNoCalleeInformation = true; + auto &&Fun = ICF->getFunctionOf(n); + auto GetNextUse = [this, &Fun, &n](n_t nPrime, ByConstRef d3) { + if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) { + return psr::unwrapNullable(PSR_FWD(NextUser)); + } + + return nPrime; + }; + // for each possible callee for (f_t SCalledProcN : Callees) { // still line 14 // check if a special summary for the called procedure exists @@ -405,8 +442,10 @@ class IDESolver "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)); + + auto DestN = GetNextUse(ReturnSiteN, d3); + WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)), + IDEProblem.extend(f, SumEdgFnE)); } } } else { @@ -498,15 +537,17 @@ class IDESolver << f4); PHASAR_LOG_LEVEL(DEBUG, " (return * calleeSummary * call)"); - EdgeFunction fPrime = - f4.composeWith(fCalleeSummary).composeWith(f5); + EdgeFunction fPrime = IDEProblem.extend( + IDEProblem.extend(f4, fCalleeSummary), f5); PHASAR_LOG_LEVEL(DEBUG, " = " << fPrime); d_t d5_restoredCtx = restoreContextOnReturnedFact(n, d2, d5); // propagte the effects of the entire call PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f); + + auto DestN = GetNextUse(RetSiteN, d5_restoredCtx); WorkList.emplace_back( - PathEdge(d1, RetSiteN, std::move(d5_restoredCtx)), - f.composeWith(fPrime)); + PathEdge(d1, DestN, std::move(d5_restoredCtx)), + IDEProblem.extend(f, fPrime)); } } } @@ -538,10 +579,11 @@ class IDESolver .push_back(EdgeFnE); } INC_COUNTER("EF Queries", 1, Full); - auto fPrime = f.composeWith(EdgeFnE); + auto fPrime = IDEProblem.extend(f, EdgeFnE); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << EdgeFnE << " * " << f << " = " << fPrime); - WorkList.emplace_back(PathEdge(d1, ReturnSiteN, std::move(d3)), + auto DestN = GetNextUse(ReturnSiteN, d3); + WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)), std::move(fPrime)); } } @@ -559,6 +601,8 @@ class IDESolver EdgeFunction f = jumpFunction(Edge); auto [d1, n, d2] = Edge.consume(); + const auto &Fun = ICF->getFunctionOf(n); + for (const auto nPrime : ICF->getSuccsOf(n)) { FlowFunctionPtrType FlowFunc = CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); @@ -570,15 +614,24 @@ class IDESolver EdgeFunction g = CachedFlowEdgeFunctions.getNormalEdgeFunction(n, d2, nPrime, d3); PHASAR_LOG_LEVEL(DEBUG, "Queried Normal Edge Function: " << g); - EdgeFunction fPrime = f.composeWith(g); + EdgeFunction fPrime = IDEProblem.extend(f, g); + + auto DestN = [&, &n = n] { + if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) { + return psr::unwrapNullable(PSR_FWD(NextUser)); + } + + return nPrime; + }(); + if (SolverConfig.emitESG()) { - IntermediateEdgeFunctions[std::make_tuple(n, d2, nPrime, d3)] + IntermediateEdgeFunctions[std::make_tuple(n, d2, DestN, d3)] .push_back(g); } PHASAR_LOG_LEVEL(DEBUG, "Compose: " << g << " * " << f << " = " << fPrime); INC_COUNTER("EF Queries", 1, Full); - WorkList.emplace_back(PathEdge(d1, nPrime, std::move(d3)), + WorkList.emplace_back(PathEdge(d1, DestN, std::move(d3)), std::move(fPrime)); } } @@ -911,6 +964,7 @@ class IDESolver for (const auto &Entry : Inc) { // line 22 n_t c = Entry.first; + auto &&Fun = ICF->getFunctionOf(c); // for each return site for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(c)) { // compute return-flow function @@ -950,7 +1004,8 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f << " * " << f4); PHASAR_LOG_LEVEL(DEBUG, " (return * function * call)"); - EdgeFunction fPrime = f4.composeWith(f).composeWith(f5); + EdgeFunction fPrime = + IDEProblem.extend(IDEProblem.extend(f4, f), f5); PHASAR_LOG_LEVEL(DEBUG, " = " << fPrime); // for each jump function coming into the call, propagate to // return site using the composed function @@ -963,9 +1018,19 @@ class IDESolver d_t d3 = ValAndFunc.first; d_t d5_restoredCtx = restoreContextOnReturnedFact(c, d4, d5); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << fPrime << " * " << f3); - WorkList.emplace_back(PathEdge(std::move(d3), RetSiteC, - std::move(d5_restoredCtx)), - f3.composeWith(fPrime)); + + auto DestN = [&] { + if (auto &&NextUser = + getNextUserOrNull(Fun, d5_restoredCtx, c)) { + return psr::unwrapNullable(PSR_FWD(NextUser)); + } + + return RetSiteC; + }(); + + WorkList.emplace_back( + PathEdge(std::move(d3), DestN, std::move(d5_restoredCtx)), + IDEProblem.extend(f3, fPrime)); } } } @@ -1004,7 +1069,7 @@ class IDESolver } INC_COUNTER("EF Queries", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f); - propagteUnbalancedReturnFlow(RetSiteC, d5, f.composeWith(f5), + propagteUnbalancedReturnFlow(RetSiteC, d5, IDEProblem.extend(f, f5), Caller); // register for value processing (2nd IDE phase) UnbalancedRetSites.insert(RetSiteC); @@ -1153,7 +1218,7 @@ class IDESolver // was found return AllTop; }(); - EdgeFunction fPrime = JumpFnE.joinWith(f); + EdgeFunction fPrime = IDEProblem.combine(JumpFnE, f); bool NewFunction = fPrime != JumpFnE; IF_LOG_LEVEL_ENABLED(DEBUG, { @@ -1804,7 +1869,10 @@ class IDESolver IDETabulationProblem &IDEProblem; d_t ZeroValue; const i_t *ICF; + const void *SVFG; IFDSIDESolverConfig &SolverConfig; + Nullable (*NextUserOrNullCB)(const void *, ByConstRef, + ByConstRef, ByConstRef) = nullptr; std::vector, EdgeFunction>> WorkList; std::vector> ValuePropWL; @@ -1856,6 +1924,11 @@ IDESolver(Problem &, ICF *) -> IDESolver; +template +IDESolver(Problem *, ICF *) + -> IDESolver; + template using IDESolver_P = IDESolver; @@ -1866,7 +1939,7 @@ OwningSolverResults solveIDEProblem(IDETabulationProblem &Problem, const typename AnalysisDomainTy::i_t &ICF) { - IDESolver Solver(Problem, &ICF); + IDESolver Solver(&Problem, &ICF); Solver.solve(); return Solver.consumeSolverResults(); } diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index 3a5f20d8f0..e6423bab0e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -21,7 +21,6 @@ #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/Domain/BinaryDomain.h" -#include #include #include #include @@ -38,11 +37,17 @@ class IFDSSolver using n_t = typename AnalysisDomainTy::n_t; using i_t = typename AnalysisDomainTy::i_t; - template >> IFDSSolver(IFDSTabulationProblem &IFDSProblem, - const i_t *ICF) + const I *ICF) + : IDESolver>(IFDSProblem, ICF) {} + template >> + IFDSSolver(IFDSTabulationProblem *IFDSProblem, + const I *ICF) : IDESolver>(IFDSProblem, ICF) {} ~IFDSSolver() override = default; @@ -101,6 +106,10 @@ template IFDSSolver(Problem &, ICF *) -> IFDSSolver; +template +IFDSSolver(Problem *, ICF *) + -> IFDSSolver; template using IFDSSolver_P = IFDSSolver OwningSolverResults + typename AnalysisDomainTy::d_t, BinaryDomain> solveIFDSProblem(IFDSTabulationProblem &Problem, const typename AnalysisDomainTy::i_t &ICF) { IFDSSolver Solver(Problem, &ICF); diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h b/include/phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h new file mode 100644 index 0000000000..0ca1e8f25d --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h @@ -0,0 +1,182 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDBASEDSOLVERRESULTS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_IDBASEDSOLVERRESULTS_H + +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h" +#include "phasar/Utils/ByRef.h" + +#include "llvm/ADT/STLExtras.h" + +#include +#include +#include +#include +#include + +namespace psr { + +template class IdBasedSolverResults { + using row_map_t = + typename detail::IterativeIDESolverResults::ValTab_value_type; + +public: + using n_t = N; + using d_t = D; + using l_t = L; + + class RowView { + struct Transformator { + const detail::IterativeIDESolverResults *Results{}; + mutable std::optional> Cache{}; + + const std::pair & + operator()(ByConstRef Entry) const { + Cache = std::make_pair(Results->FactCompressor[Entry.first], + Results->ValCompressor[Entry.second]); + return *Cache; + } + }; + + public: + using iterator = llvm::mapped_iterator; + using const_iterator = iterator; + using difference_type = ptrdiff_t; + + explicit RowView( + const detail::IterativeIDESolverResults *Results, + const row_map_t *Row) noexcept + : Results(Results), Row(Row) { + assert(Results != nullptr); + assert(Row != nullptr); + } + + [[nodiscard]] const_iterator begin() const noexcept { + return llvm::map_iterator(Row->cells().begin(), Transformator{Results}); + } + + [[nodiscard]] const_iterator end() const noexcept { + return llvm::map_iterator(Row->cells().end(), Transformator{Results}); + } + + [[nodiscard]] const_iterator find(ByConstRef Fact) const { + auto FactId = Results->FactCompressor.getOrNull(Fact); + if (!FactId) { + return end(); + } + return llvm::map_iterator(Row->find(*FactId), Transformator{Results}); + } + + [[nodiscard]] bool count(ByConstRef Fact) const { + auto FactId = Results->FactCompressor.getOrNull(Fact); + if (!FactId) { + return false; + } + return Row->contains(*FactId); + } + + [[nodiscard]] size_t size() const noexcept { return Row->size(); } + + private: + const detail::IterativeIDESolverResults *Results{}; + const row_map_t *Row{}; + }; + + explicit IdBasedSolverResults( + const detail::IterativeIDESolverResults *Results) noexcept + : Results(Results) { + assert(Results != nullptr); + } + + [[nodiscard]] l_t resultAt(ByConstRef Stmt, ByConstRef Node) const { + auto NodeId = Results->NodeCompressor.getOrNull(Stmt); + auto FactId = Results->FactCompressor.getOrNull(Node); + + if (!NodeId || !FactId) { + return l_t{}; + } + + const auto &Entry = Results->ValTab[size_t(*NodeId)]; + auto RetIt = Entry.find(*FactId); + if (RetIt == Entry.cells().end()) { + return l_t{}; + } + + return Results->ValCompressor[RetIt->second]; + } + + [[nodiscard]] std::unordered_map + resultsAt(ByConstRef Stmt, bool StripZero = false) const { + auto NodeId = Results->NodeCompressor.getOrNull(Stmt); + if (!NodeId) { + return {}; + } + + std::unordered_map Result; + Result.reserve(Results->ValTab[size_t(*NodeId)].size()); + for (auto [Fact, Value] : Results->ValTab[size_t(*NodeId)].cells()) { + /// In the IterativeIDESolver, we have made sure that the zero flow-fact + /// always has the Id 0 + if (StripZero && Fact == 0) { + continue; + } + Result.try_emplace(Results->FactCompressor[Fact], + Results->ValCompressor[Value]); + } + + return Result; + } + + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { + auto NodeId = Results->NodeCompressor.getOrNull(Stmt); + if (!NodeId) { + return {}; + } + + std::set Result; + for (auto [Fact, Unused] : Results->ValTab[size_t(*NodeId)]) { + Result.insert(Results->FactCompressor[Fact]); + } + return Result; + } + + [[nodiscard]] size_t size() const noexcept { + assert(Results->ValTab.size() >= Results->NodeCompressor.size()); + return Results->NodeCompressor.size(); + } + + [[nodiscard]] auto getAllResultEntries() const noexcept { + auto Txn = + [Results{this->Results}](const auto &Entry) -> std::pair { + const auto &[First, Second] = Entry; + return std::make_pair(First, RowView(Results, &Second)); + }; + + return llvm::map_range(llvm::zip(Results->NodeCompressor, Results->ValTab), + Txn); + } + + [[nodiscard]] bool containsNode(ByConstRef Stmt) const { + return Results->NodeCompressor.getOrNull(Stmt) != std::nullopt; + } + + [[nodiscard]] RowView row(ByConstRef Stmt) const { + auto NodeId = Results->NodeCompressor.getOrNull(Stmt); + assert(NodeId); + return RowView(Results, &Results->ValTab[*NodeId]); + } + + template + void foreachResultEntry(HandlerFn Handler) const { + for (const auto &[Row, RowMap] : getAllResultEntries()) { + for (const auto &[Col, Val] : RowMap) { + std::invoke(Handler, std::make_tuple(Row, Col, Val)); + } + } + } + +private: + const detail::IterativeIDESolverResults *Results{}; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IDBASEDSOLVERRESULTS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h new file mode 100644 index 0000000000..bab1509d0d --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -0,0 +1,1330 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVER_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVER_H + +#include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" +#include "phasar/DataFlow/IfdsIde/Solver/Compressor.h" +#include "phasar/DataFlow/IfdsIde/Solver/EdgeFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h" +#include "phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h" +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h" +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h" +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverStats.h" +#include "phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h" +#include "phasar/DataFlow/IfdsIde/Solver/WorkListTraits.h" +#include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/StableVector.h" +#include "phasar/Utils/TableWrappers.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace psr { + +/// Solves the given IDETabulationProblem as described in the 1996 paper by +/// Sagiv, Horwitz and Reps. To solve the problem, call solve(). +/// +/// This solver implements the optimizations and the $JF_N$ layout from the +/// paper "Scaling Interprocedural Static Data-Flow Analysis to Large C/C++ +/// Applications: An Experience Report" +/// (https://doi.org/10.4230/LIPIcs.ECOOP.2024.36) by Schiebel, Sattler, +/// Schubert, Apel, and Bodden. +template > +class IterativeIDESolver + : private SolverStatsSelector, + private detail::IterativeIDESolverResults< + typename ProblemTy::ProblemAnalysisDomain::n_t, + typename ProblemTy::ProblemAnalysisDomain::d_t, + std::conditional_t>, + public IterativeIDESolverBase< + StaticSolverConfigTy, + typename StaticSolverConfigTy::template EdgeFunctionPtrType< + typename ProblemTy::ProblemAnalysisDomain::l_t>> { +public: + using domain_t = typename ProblemTy::ProblemAnalysisDomain; + using d_t = typename domain_t::d_t; + using n_t = typename domain_t::n_t; + using f_t = typename domain_t::f_t; + using t_t = typename domain_t::t_t; + using v_t = typename domain_t::v_t; + using l_t = std::conditional_t; + using i_t = typename domain_t::i_t; + + using config_t = StaticSolverConfigTy; + +private: + using base_t = IterativeIDESolverBase< + StaticSolverConfigTy, + typename StaticSolverConfigTy::template EdgeFunctionPtrType< + typename domain_t::l_t>>; + + using base_results_t = detail::IterativeIDESolverResults; + + using base_t::ComputeValues; + using base_t::EnableStatistics; + static constexpr bool UseEndSummaryTab = config_t::UseEndSummaryTab; + using typename base_t::EdgeFunctionPtrType; + using typename base_t::InterPropagationJob; + using typename base_t::InterPropagationJobRef; + using typename base_t::InterPropagationJobRefDSI; + using typename base_t::PropagationJob; + using typename base_t::SummaryEdge; + using typename base_t::SummaryEdges; + using typename base_t::SummaryEdges_JF1; + using typename base_t::ValuePropagationJob; + + using base_results_t::FactCompressor; + using base_results_t::NodeCompressor; + using base_results_t::ValCompressor; + + template + using map_t = typename base_t::template map_t; + + template using set_t = typename base_t::template set_t; + + template + using worklist_t = typename base_t::template worklist_t; + + using flow_edge_function_cache_t = + typename base_t::template flow_edge_function_cache_t< + ProblemTy, StaticSolverConfigTy::AutoAddZero>; + + using typename base_t::summaries_t; + + static inline constexpr JumpFunctionGCMode EnableJumpFunctionGC = + StaticSolverConfigTy::EnableJumpFunctionGC; + + static inline ProblemTy &assertNotNull(ProblemTy *Problem) noexcept { + /// Dereferencing a nullptr is UB, so after initializing this->Problem the + /// null-check might be optimized away to the literal 'true'. + /// However, we still want to pass a pointer to the ctor to make clear that + /// the _reference_ of the problem is captured. + assert(Problem && + "IterativeIDESolver: The IDETabulationProblem must not be null!"); + return *Problem; + } + + static inline const i_t &assertNotNull(const i_t *ICFG) noexcept { + /// Dereferencing a nullptr is UB, so after initializing this->ICFG the + /// null-check might be optimized away to the literal 'true'. + /// However, we still want to pass a pointer to the ctor to make clear that + /// the _reference_ of the problem is captured. + assert(ICFG && "IterativeIDESolver: The ICFG must not be null!"); + return *ICFG; + } + +public: + IterativeIDESolver(ProblemTy *Problem, const i_t *ICFG) noexcept + : Problem(assertNotNull(Problem)), ICFG(assertNotNull(ICFG)) {} + + void solve() { + const auto NumInsts = Problem.getProjectIRDB()->getNumInstructions(); + const auto NumFuns = Problem.getProjectIRDB()->getNumFunctions(); + + NodeCompressor = + NodeCompressorTraits::create(Problem.getProjectIRDB()); + + JumpFunctions.reserve(NumInsts); + this->base_results_t::ValTab.reserve(NumInsts); + + /// Initial size of 64 is too much for jump functions per instruction; 16 + /// should be better: + for (size_t I = 0; I != NumInsts; ++I) { + JumpFunctions.emplace_back(); + this->base_results_t::ValTab.emplace_back().reserve(16); + } + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + RefCountPerFunction = llvm::OwningArrayRef(NumFuns); + std::uninitialized_fill_n(RefCountPerFunction.data(), + RefCountPerFunction.size(), 0); + CandidateFunctionsForGC.resize(NumFuns); + } + + NodeCompressor.reserve(NumInsts); + FactCompressor.reserve(NumInsts); + FunCompressor.reserve(NumFuns); + FECache.reserve(NumInsts, ICFG.getNumCallSites(), NumFuns); + /// Make sure, that the Zero-flowfact always has the ID 0 + FactCompressor.getOrInsert(Problem.getZeroValue()); + + performDataflowFactPropagation(); + + /// Finished Phase I, now go for Phase II if necessary + performValuePropagation(); + } + + [[nodiscard]] IdBasedSolverResults + getSolverResults() const noexcept { + return IdBasedSolverResults(this); + } + + void dumpResults(llvm::raw_ostream &OS = llvm::outs()) const { + OS << "\n***************************************************************\n" + << "* Raw IDESolver results *\n" + << "***************************************************************\n"; + auto Cells = this->base_results_t::ValTab_cellVec(); + if (Cells.empty()) { + OS << "No results computed!\n"; + return; + } + + 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 Curr{}; + f_t PrevFn{}; + f_t CurrFn{}; + + for (const auto &Cell : Cells) { + Curr = Cell.getRowKey(); + CurrFn = ICFG.getFunctionOf(Curr); + if (PrevFn != CurrFn) { + PrevFn = CurrFn; + OS << "\n\n============ Results for function '" + + ICFG.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(Cell.getColumnKey()); + if constexpr (ComputeValues) { + OS << " | V: " << LToString(Cell.getValue()); + } + + OS << '\n'; + } + + OS << '\n'; + } + + template > + [[nodiscard]] IterativeIDESolverStats getStats() const noexcept { + return *this; + } + + template > + void dumpStats(llvm::raw_ostream &OS = llvm::outs()) const { + OS << getStats(); + } + +private: + void performDataflowFactPropagation() { + submitInitialSeeds(); + + std::atomic_bool Finished = true; + do { + /// NOTE: Have a separate function on the worklist to process it, to + /// allow for easier integration with task-pools + WorkList.processEntriesUntilEmpty([this, &Finished](PropagationJob Job) { + /// propagate only handles intra-edges as of now - add separate + /// functionality to handle inter-edges as well + propagate(Job.AtInstruction, Job.SourceFact, Job.PropagatedFact, + std::move(Job.SourceEF)); + bool Dummy = true; + Finished.compare_exchange_strong( + Dummy, false, std::memory_order_release, std::memory_order_relaxed); + }); + +#ifndef NDEBUG + // Sanity checks + if (llvm::any_of(RefCountPerFunction, [](auto RC) { return RC != 0; })) { + llvm::report_fatal_error( + "Worklist.empty() does not imply Function ref-counts==0 ?"); + } + + if (!WorkList.empty()) { + llvm::report_fatal_error( + "Worklist should be empty after processing all items"); + } +#endif // NDEBUG + + assert(WorkList.empty() && + "Worklist should be empty after processing all items"); + + processInterJobs(); + + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + /// CAUTION: The functions from the CallWL also need to be considered + /// live! We therefore need to be careful when applying this GC in a + /// multithreaded environment + + runGC(); + } + + } while (Finished.exchange(true, std::memory_order_acq_rel) == false); + + if constexpr (EnableStatistics) { + this->FEStats = FECache.getStats(); + this->NumAllInterPropagations = AllInterPropagations.size(); + this->AllInterPropagationsBytes = + AllInterPropagations.getApproxSizeInBytes() + + AllInterPropagationsOwner.getApproxSizeInBytes(); + this->SourceFactAndCSToInterJobSize = SourceFactAndCSToInterJob.size(); + this->SourceFactAndCSToInterJobBytes = + SourceFactAndCSToInterJob.getApproxSizeInBytes(); + this->SourceFactAndFuncToInterJobSize = + SourceFactAndFuncToInterJob.size(); + this->SourceFactAndFuncToInterJobBytes = + SourceFactAndFuncToInterJob.getApproxSizeInBytes(); + this->NumFlowFacts = FactCompressor.size(); + this->InstCompressorCapacity = NodeCompressor.capacity(); + this->FactCompressorCapacity = FactCompressor.capacity(); + this->FunCompressorCapacity = FunCompressor.capacity(); + + this->JumpFunctionsMapBytes = JumpFunctions.capacity_in_bytes(); + this->CumulESGEdges = 0; + this->MaxESGEdgesPerInst = 0; + for (const auto &JF : JumpFunctions) { + this->JumpFunctionsMapBytes += JF.getApproxSizeInBytes(); + this->CumulESGEdges += JF.size(); + this->MaxESGEdgesPerInst = + std::max(this->MaxESGEdgesPerInst, JF.size()); + } + + this->ValTabBytes = this->base_results_t::ValTab.capacity_in_bytes(); + for (const auto &FactsVals : this->base_results_t::ValTab) { + this->ValTabBytes += FactsVals.getApproxSizeInBytes(); + } + + this->AvgESGEdgesPerInst = + double(this->CumulESGEdges) / JumpFunctions.size(); + + if constexpr (UseEndSummaryTab) { + this->EndSummaryTabSize = EndSummaryTab.getApproxSizeInBytes(); + this->NumEndSummaries = 0; + for (const auto &Summary : EndSummaryTab.cells()) { + this->NumEndSummaries += Summary.second.size(); + this->EndSummaryTabSize += Summary.second.getMemorySize(); + } + } + } + + FECache.clearFlowFunctions(); + SourceFactAndFuncToInterJob.clear(); + WorkList.clear(); + CallWL.clear(); + } + + void performValuePropagation() { + if constexpr (ComputeValues) { + /// NOTE: We can already clear the EFCache here, as we are not querying + /// any edge function in Phase II; The EFs that are in use are kept alive + /// by their shared_ptr + FECache.clear(); + + submitInitialValues(); + WLProp.processEntriesUntilEmpty([this](ValuePropagationJob Job) { + propagateValue(Job.Inst, Job.Fact, std::move(Job.Value)); + }); + + WLProp.clear(); + + for (uint32_t SP : WLComp) { + computeValues(SP); + } + + if constexpr (EnableStatistics) { + this->WLCompHighWatermark = WLComp.size(); + } + + WLComp.clear(); + } + + JumpFunctions.clear(); + SourceFactAndCSToInterJob.clear(); + AllInterPropagations.clear(); + AllInterPropagationsOwner.clear(); + } + + void submitInitialSeeds() { + auto Seeds = Problem.initialSeeds(); + EdgeFunctionPtrType IdFun = [] { + if constexpr (ComputeValues) { + return EdgeIdentity{}; + } else { + return EdgeFunctionPtrType{}; + } + }(); + + for (const auto &[Inst, SeedMap] : Seeds.getSeeds()) { + auto InstId = NodeCompressor.getOrInsert(Inst); + auto Fun = FunCompressor.getOrInsert(ICFG.getFunctionOf(Inst)); + for (const auto &[Fact, Val] : SeedMap) { + auto FactId = FactCompressor.getOrInsert(Fact); + auto &JumpFns = JumpFunctions[InstId]; + + storeResultsAndPropagate(JumpFns, InstId, FactId, FactId, Fun, IdFun); + } + } + } + + template + [[nodiscard]] bool + keepAnalysisInformationAt(ByConstRef Inst) const noexcept { + if (ICFG.isExitInst(Inst) || ICFG.isStartPoint(Inst)) { + /// Keep the procedure summaries + starting points, such that + /// already analyzed paths are not analyzed again + return true; + } + + if (llvm::any_of(ICFG.getPredsOf(Inst), [ICF{&ICFG}](ByConstRef Pred) { + return ICF->isCallSite(Pred); + })) { + /// Keep the return-sites + return true; + } + + if constexpr (has_isInteresting_v && + (Set || (!Set && ComputeValues))) { + if (Problem.isInteresting(Inst)) { + /// Keep analysis information at interesting instructions + return true; + } + } + return false; + } + + ByConstRef get(uint32_t Inst, uint32_t Fact) { + return ValCompressor[this->base_results_t::ValTab[size_t(Inst)].getOrCreate( + Fact)]; + } + + void set(uint32_t Inst, uint32_t Fact, l_t Val) { + if constexpr (EnableJumpFunctionGC == + JumpFunctionGCMode::EnabledAggressively) { + if (!keepAnalysisInformationAt(NodeCompressor[Inst])) { + return; + } + } + + auto &Dest = this->base_results_t::ValTab[size_t(Inst)].getOrCreate(Fact); + if constexpr (!std::is_const_v>) { + Dest = ValCompressor.getOrInsert(std::move(Val)); + } + } + + template + std::enable_if_t + storeResultsAndPropagate(SummaryEdges &JumpFns, uint32_t SuccId, + uint32_t SourceFact, uint32_t LocalFact, + uint32_t FunId, EdgeFunctionPtrType LocalEF) { + auto &EF = JumpFns.getOrCreate(combineIds(SourceFact, LocalFact)); + if (!EF) { + EF = std::move(LocalEF); + /// Register new propagation job as we haven't seen this + /// fact here yet + + WorkList.emplace(PropagationJob{EF, SuccId, SourceFact, LocalFact}); + + set(SuccId, LocalFact, Problem.topElement()); + + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + RefCountPerFunction[FunId]++; + CandidateFunctionsForGC.set(FunId); + } + + if constexpr (EnableStatistics) { + this->NumPathEdges++; + if (this->NumPathEdges > this->NumPathEdgesHighWatermark) { + this->NumPathEdgesHighWatermark = this->NumPathEdges; + } + if (WorkList.size() > this->WorkListHighWatermark) { + this->WorkListHighWatermark = WorkList.size(); + } + } + return true; + } + + auto NewEF = EF.joinWith(std::move(LocalEF)); + assert(NewEF != nullptr); + + if (NewEF != EF) { + /// Register new propagation job as we have refined the + /// edge-function + EF = NewEF; + WorkList.emplace( + PropagationJob{std::move(NewEF), SuccId, SourceFact, LocalFact}); + + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + RefCountPerFunction[FunId]++; + CandidateFunctionsForGC.set(FunId); + } + return true; + } + return false; + } + template + std::enable_if_t + storeResultsAndPropagate(SummaryEdges &JumpFns, uint32_t SuccId, + uint32_t SourceFact, uint32_t LocalFact, + uint32_t FunId, EdgeFunctionPtrType /*LocalEF*/) { + if (JumpFns.insert(combineIds(SourceFact, LocalFact)).second) { + WorkList.emplace(PropagationJob{{}, SuccId, SourceFact, LocalFact}); + + set(SuccId, LocalFact, BinaryDomain::TOP); + + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + RefCountPerFunction[FunId]++; + + CandidateFunctionsForGC.set(FunId); + } + if constexpr (EnableStatistics) { + this->NumPathEdges++; + if (this->NumPathEdges > this->NumPathEdgesHighWatermark) { + this->NumPathEdgesHighWatermark = this->NumPathEdges; + } + if (WorkList.size() > this->WorkListHighWatermark) { + this->WorkListHighWatermark = WorkList.size(); + } + } + return true; + } + return false; + } + + template + std::enable_if_t storeSummary(SummaryEdges_JF1 &JumpFns, + uint32_t LocalFact, + EdgeFunctionPtrType LocalEF) { + auto &EF = JumpFns[LocalFact]; + if (!EF) { + EF = std::move(LocalEF); + /// Register new propagation job as we haven't seen this + /// fact here yet + + if constexpr (EnableStatistics) { + this->NumPathEdges++; + if (this->NumPathEdges > this->NumPathEdgesHighWatermark) { + this->NumPathEdgesHighWatermark = this->NumPathEdges; + } + } + return; + } + + auto NewEF = EF.joinWith(std::move(LocalEF)); + assert(NewEF != nullptr); + + if (NewEF != EF) { + /// Register new propagation job as we have refined the + /// edge-function + EF = NewEF; + } + } + + template + std::enable_if_t storeSummary(SummaryEdges_JF1 &JumpFns, + uint32_t LocalFact, + EdgeFunctionPtrType /*LocalEF*/) { + if (JumpFns.insert({LocalFact, {}}).second) { + if constexpr (EnableStatistics) { + this->NumPathEdges++; + if (this->NumPathEdges > this->NumPathEdgesHighWatermark) { + this->NumPathEdgesHighWatermark = this->NumPathEdges; + } + } + } + } + + void propagate(uint32_t AtInstructionId, uint32_t SourceFact, + uint32_t PropagatedFact, EdgeFunctionPtrType SourceEF) { + + auto AtInstruction = NodeCompressor[AtInstructionId]; + + auto FunId = [=] { + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + + auto Ret = FunCompressor.getOrInsert(ICFG.getFunctionOf(AtInstruction)); + + assert(RefCountPerFunction[Ret] > 0); + RefCountPerFunction[Ret]--; + return Ret; + } else { + (void)this; + (void)AtInstruction; + return 0; + } + }(); + + applyFlowFunction(AtInstruction, AtInstructionId, SourceFact, + PropagatedFact, FunId, std::move(SourceEF)); + } + + void applyFlowFunction(ByConstRef AtInstruction, + uint32_t AtInstructionId, uint32_t SourceFactId, + uint32_t PropagatedFactId, uint32_t FunId, + EdgeFunctionPtrType SourceEF) { + + if (!ICFG.isCallSite(AtInstruction)) { + applyNormalFlow(AtInstruction, AtInstructionId, SourceFactId, + PropagatedFactId, std::move(SourceEF), FunId); + } else { + applyIntraCallFlow(AtInstruction, AtInstructionId, SourceFactId, + PropagatedFactId, std::move(SourceEF), FunId); + } + } + + void applyNormalFlow(ByConstRef AtInstruction, uint32_t AtInstructionId, + uint32_t SourceFactId, uint32_t PropagatedFactId, + EdgeFunctionPtrType SourceEF, uint32_t FunId) { + + auto CSFact = FactCompressor[PropagatedFactId]; + + for (ByConstRef Succ : ICFG.getSuccsOf(AtInstruction)) { + auto SuccId = NodeCompressor.getOrInsert(Succ); + auto Facts = + FECache + .getNormalFlowFunction(Problem, AtInstruction, Succ, + combineIds(AtInstructionId, SuccId)) + .computeTargets(CSFact); + + auto &JumpFns = JumpFunctions[SuccId]; + + for (ByConstRef Fact : Facts) { + auto FactId = FactCompressor.getOrInsert(Fact); + auto EF = [&] { + if constexpr (ComputeValues) { + return SourceEF.composeWith(FECache.getNormalEdgeFunction( + Problem, AtInstruction, CSFact, Succ, Fact, + combineIds(AtInstructionId, SuccId), + combineIds(PropagatedFactId, FactId))); + } else { + return EdgeFunctionPtrType{}; + } + }(); + + storeResultsAndPropagate(JumpFns, SuccId, SourceFactId, FactId, FunId, + std::move(EF)); + } + } + + /// NOTE: Is isExitInst, we did not enter the above loop. So, we can only + /// have reached here, if the PropagatedFact (or at least the SourceEF) was + /// new at AtInstruction. + if (ICFG.isExitInst(AtInstruction)) { + if constexpr (EnableJumpFunctionGC == JumpFunctionGCMode::Disabled) { + auto Fun = ICFG.getFunctionOf(AtInstruction); + FunId = FunCompressor.getOrInsert(std::move(Fun)); + } + + /// These call-sites might have already been processed, but since we have + /// now new summary information, we must reschedule them all for + /// processing in Step3 + CallWL.insert(combineIds(SourceFactId, FunId)); + + if constexpr (UseEndSummaryTab) { + storeSummary(EndSummaryTab.getOrCreate(combineIds(FunId, SourceFactId)), + PropagatedFactId, std::move(SourceEF)); + } + } + } + + void applyIntraCallFlow(ByConstRef AtInstruction, + uint32_t AtInstructionId, uint32_t SourceFactId, + uint32_t PropagatedFactId, + EdgeFunctionPtrType SourceEF, uint32_t FunId) { + const auto &Callees = ICFG.getCalleesOfCallAt(AtInstruction); + + applyCallToReturnFlow(AtInstruction, AtInstructionId, SourceFactId, + PropagatedFactId, SourceEF, Callees, FunId); + + handleOrDeferCallFlow(AtInstruction, AtInstructionId, SourceFactId, + PropagatedFactId, std::move(SourceEF), Callees, + FunId); + } + + template + void applyCallToReturnFlow(ByConstRef AtInstruction, + uint32_t AtInstructionId, uint32_t SourceFactId, + uint32_t PropagatedFactId, + EdgeFunctionPtrType SourceEF, + const CalleesTy &Callees, uint32_t FunId) { + auto CSFact = FactCompressor[PropagatedFactId]; + + for (ByConstRef RetSite : ICFG.getReturnSitesOfCallAt(AtInstruction)) { + auto RetSiteId = NodeCompressor.getOrInsert(RetSite); + auto Facts = FECache + .getCallToRetFlowFunction( + Problem, AtInstruction, RetSite, Callees /*Vec*/, + combineIds(AtInstructionId, RetSiteId)) + .computeTargets(CSFact); + + auto &JumpFns = JumpFunctions[RetSiteId]; + + for (ByConstRef Fact : Facts) { + auto FactId = FactCompressor.getOrInsert(Fact); + + auto EF = [&] { + if constexpr (ComputeValues) { + return SourceEF.composeWith(FECache.getCallToRetEdgeFunction( + Problem, AtInstruction, CSFact, RetSite, Fact, Callees /*Vec*/, + combineIds(AtInstructionId, RetSiteId), + combineIds(PropagatedFactId, FactId))); + } else { + return EdgeFunctionPtrType{}; + } + }(); + + storeResultsAndPropagate(JumpFns, RetSiteId, SourceFactId, FactId, + FunId, std::move(EF)); + } + } + + /// NOTE: A CallSite can never be an exit-inst + } + + template + void handleOrDeferCallFlow(ByConstRef AtInstruction, + uint32_t AtInstructionId, uint32_t SourceFactId, + uint32_t PropagatedFactId, + EdgeFunctionPtrType SourceEF, + const CalleesTy &Callees, uint32_t FunId) { + auto CSFact = FactCompressor[PropagatedFactId]; + for (ByConstRef Callee : Callees) { + auto CalleeId = FunCompressor.getOrInsert(Callee); + auto SummaryFF = + FECache.getSummaryFlowFunction(Problem, AtInstruction, Callee, + combineIds(AtInstructionId, CalleeId)); + + if (SummaryFF == nullptr) { + /// No summary. Start inTRA propagation for the callee and defer + /// return-propagation + deferCallFlow(AtInstruction, AtInstructionId, SourceFactId, CSFact, + PropagatedFactId, SourceEF, Callee, CalleeId, FunId); + } else { + /// Apply SummaryFF and ignore this CSCallee pair in the + /// inter-propagation + applySummaryFlow(SummaryFF.computeTargets(CSFact), AtInstruction, + AtInstructionId, SourceFactId, CSFact, + PropagatedFactId, SourceEF, FunId); + } + } + } + + void countSummaryLinearSearch(size_t SearchLen, size_t NumSummaries) { + if constexpr (UseEndSummaryTab) { + SearchLen = NumSummaries; + } + + if constexpr (EnableStatistics) { + this->TotalNumLinearSearchForSummary++; + this->CumulLinearSearchLenForSummary += SearchLen; + this->MaxLenLinearSearchForSummary = + std::max(this->MaxLenLinearSearchForSummary, SearchLen); + this->CumulDiffNumSummariesFound += (SearchLen - NumSummaries); + this->MaxDiffNumSummariesFound = + std::max(this->MaxDiffNumSummariesFound, (SearchLen - NumSummaries)); + + this->CumulRelDiffNumSummariesFound += + SearchLen ? double(NumSummaries) / double(SearchLen) : 1; + } + } + + void applyEarlySummariesAtCall(ByConstRef AtInstruction, + uint32_t AtInstructionId, + ByConstRef Callee, uint32_t CalleeId, + uint32_t FactId, uint32_t SourceFactId, + uint32_t FunId, EdgeFunctionPtrType CallEF) { + /* if (HasResults)*/ { + /// Lines 15.2-15.6 in Naeem's paper: + auto CallerId = [this, FunId, AtInstruction] { + if constexpr (EnableJumpFunctionGC != JumpFunctionGCMode::Disabled) { + (void)this; + (void)AtInstruction; + return FunId; + } else { + (void)FunId; + return FunCompressor.getOrInsert(ICFG.getFunctionOf(AtInstruction)); + } + }(); + + summaries_t Summaries; + if constexpr (UseEndSummaryTab) { + const auto &SummariesTab = + EndSummaryTab.getOrCreate(combineIds(CalleeId, FactId)); + Summaries = summaries_t(SummariesTab.begin(), SummariesTab.end()); + } + + for (ByConstRef ExitInst : ICFG.getExitPointsOf(Callee)) { + auto ExitId = NodeCompressor.getOrInsert(ExitInst); + + if constexpr (!UseEndSummaryTab) { + // Summaries = JumpFunctions[ExitId].cellVec([FactId](const auto &Kvp) + // { + // return splitId(Kvp.first).first == FactId; + // }); + Summaries = JumpFunctions[ExitId].allOf( + [](uint64_t Key) { return splitId(Key).first; }, FactId, + [](uint64_t Key) { return splitId(Key).second; }); + } + + countSummaryLinearSearch(JumpFunctions[ExitId].size(), + Summaries.size()); + + if (!Summaries.empty()) { + propagateProcedureSummaries(Summaries, AtInstruction, AtInstructionId, + Callee, CalleeId, ExitInst, ExitId, + SourceFactId, CallerId, CallEF); + } + } + } + } + + void deferCallFlow(ByConstRef AtInstruction, uint32_t AtInstructionId, + uint32_t SourceFactId, ByConstRef CSFact, + uint32_t CSFactId, EdgeFunctionPtrType SourceEF, + ByConstRef Callee, uint32_t CalleeId, + uint32_t FunId) { + auto CalleeFacts = + FECache + .getCallFlowFunction(Problem, AtInstruction, Callee, + combineIds(AtInstructionId, CalleeId)) + .computeTargets(CSFact); + + EdgeFunctionPtrType IdEF = [] { + if constexpr (ComputeValues) { + return EdgeIdentity{}; + } else { + return EdgeFunctionPtrType{}; + } + }(); + for (ByConstRef SP : ICFG.getStartPointsOf(Callee)) { + auto SPId = NodeCompressor.getOrInsert(SP); + auto &JumpFn = JumpFunctions[SPId]; + // bool HasResults = !JumpFn.empty(); + for (ByConstRef Fact : CalleeFacts) { + auto FactId = FactCompressor.getOrInsert(Fact); + + auto CallEF = [&] { + if constexpr (ComputeValues) { + return SourceEF.composeWith(FECache.getCallEdgeFunction( + Problem, AtInstruction, CSFact, Callee, Fact, + combineIds(AtInstructionId, CalleeId), + combineIds(CSFactId, FactId))); + } else { + return EdgeFunctionPtrType{}; + } + }(); + + storeResultsAndPropagate(JumpFn, SPId, FactId, FactId, CalleeId, IdEF); + + // CallWL.insert(combineIds(FactId, CalleeId)); + + auto It = &AllInterPropagationsOwner.emplace_back(InterPropagationJob{ + CallEF, SourceFactId, CalleeId, AtInstructionId, FactId}); + auto Inserted = + AllInterPropagations.insert(InterPropagationJobRef{It}).second; + if (Inserted) { + applyEarlySummariesAtCall(AtInstruction, AtInstructionId, Callee, + CalleeId, FactId, SourceFactId, FunId, + CallEF); + + { + /// Reverse lookup + auto *&InterJob = SourceFactAndFuncToInterJob.getOrCreate( + combineIds(FactId, CalleeId)); + It->NextWithSameSourceFactAndCallee = InterJob; + InterJob = &*It; + } + + if constexpr (ComputeValues) { + /// Forward lookup + auto *&InterJob = SourceFactAndCSToInterJob.getOrCreate( + combineIds(SourceFactId, AtInstructionId)); + It->NextWithSameSourceFactAndCS = InterJob; + InterJob = &*It; + } + } else { + /// The InterPropagationJob was already there, so we don't need to own + /// it + AllInterPropagationsOwner.pop_back(); + } + } + } + } + + template + void applySummaryFlow(const SummaryFactsTy &SummaryFacts, + ByConstRef AtInstruction, uint32_t AtInstructionId, + uint32_t SourceFactId, ByConstRef CSFact, + uint32_t CSFactId, EdgeFunctionPtrType SourceEF, + uint32_t FunId) { + for (ByConstRef RetSite : ICFG.getReturnSitesOfCallAt(AtInstruction)) { + auto RetSiteId = NodeCompressor.getOrInsert(RetSite); + auto &JumpFns = JumpFunctions[RetSiteId]; + + for (ByConstRef Fact : SummaryFacts) { + auto FactId = FactCompressor.getOrInsert(Fact); + + auto EF = [&] { + if constexpr (ComputeValues) { + auto EF = FECache.getSummaryEdgeFunction( + Problem, AtInstruction, CSFact, RetSite, Fact, + combineIds(AtInstructionId, RetSiteId), + combineIds(CSFactId, FactId)); + return EF ? SourceEF.composeWith(std::move(EF)) : SourceEF; + } else { + return EdgeFunctionPtrType{}; + } + }(); + + storeResultsAndPropagate(JumpFns, RetSiteId, SourceFactId, FactId, + FunId, std::move(EF)); + } + } + } + + void propagateProcedureSummaries(const summaries_t &Summaries, + ByConstRef CallSite, uint32_t CSId, + ByConstRef Callee, uint32_t CalleeId, + ByConstRef ExitInst, uint32_t ExitId, + uint32_t SourceFact, uint32_t CallerId, + EdgeFunctionPtrType CallEF) { + for (ByConstRef RetSite : ICFG.getReturnSitesOfCallAt(CallSite)) { + auto RSId = NodeCompressor.getOrInsert(RetSite); + auto RetFF = FECache.getRetFlowFunction( + Problem, CallSite, Callee, ExitInst, RetSite, + combineIds(CSId, ExitId), combineIds(CalleeId, RSId)); + + auto &RSJumpFns = JumpFunctions[RSId]; + + for (const auto &Summary : Summaries) { + uint32_t SummaryFactId{Summary.first}; + auto SummaryFact = FactCompressor[SummaryFactId]; + auto RetFacts = RetFF.computeTargets(SummaryFact); + for (ByConstRef RetFact : RetFacts) { + auto RetFactId = FactCompressor.getOrInsert(RetFact); + + auto EF = [&]() mutable { + if constexpr (ComputeValues) { + auto RetEF = FECache.getReturnEdgeFunction( + Problem, CallSite, Callee, ExitInst, SummaryFact, RetSite, + RetFact, ExitId, combineIds(CSId, RSId), + combineIds(SummaryFactId, RetFactId)); + return CallEF.composeWith(Summary.second) + .composeWith(std::move(RetEF)); + } else { + return EdgeFunctionPtrType{}; + } + }(); + + storeResultsAndPropagate(RSJumpFns, RSId, SourceFact, RetFactId, + CallerId, std::move(EF)); + } + } + } + } + + void processInterJobs() { + + llvm::errs() << "processInterJobs: " << CallWL.size() + << " relevant calls\n"; + + /// Here, no other job is running concurrently, so we save and reset the + /// CallWL, such that we can start concurrent jobs in the loop below + std::vector RelevantCalls(CallWL.begin(), CallWL.end()); + + scope_exit FinishedInterCalls = [] { + llvm::errs() << "> end inter calls\n"; + }; + + if constexpr (EnableStatistics) { + if (CallWL.size() > this->CallWLHighWatermark) { + this->CallWLHighWatermark = CallWL.size(); + } + } + + CallWL.clear(); + + for (auto SourceFactAndFunc : RelevantCalls) { + auto [SPFactId, CalleeId] = splitId(SourceFactAndFunc); + auto Callee = FunCompressor[CalleeId]; + + if constexpr (EnableStatistics) { + this->TotalNumRelevantCalls++; + } + + summaries_t Summaries; + if constexpr (UseEndSummaryTab) { + const auto &SummariesTab = + EndSummaryTab.getOrCreate(combineIds(CalleeId, SPFactId)); + Summaries = summaries_t(SummariesTab.begin(), SummariesTab.end()); + } + + size_t NumInterJobs = 0; + for (ByConstRef ExitInst : ICFG.getExitPointsOf(Callee)) { + auto ExitId = NodeCompressor.getOrInsert(ExitInst); + + /// Copy the JumpFns, because in the below loop we are calling + /// getOrCreate on JumpFunctions again and in a tight recursion + /// ExitId and RSId might be the same and inserting into the same + /// map we are iterating over is bad + + if constexpr (!UseEndSummaryTab) { + // Summaries = JumpFunctions[ExitId].cellVec( + // [SPFactId{SPFactId}](const auto &Kvp) { + // return splitId(Kvp.first).first == SPFactId; + // }); + Summaries = JumpFunctions[ExitId].allOf( + [](uint64_t Key) { return splitId(Key).first; }, SPFactId, + [](uint64_t Key) { return splitId(Key).second; }); + } + + countSummaryLinearSearch(JumpFunctions[ExitId].size(), + Summaries.size()); + + for (const InterPropagationJob *InterJob = + SourceFactAndFuncToInterJob.getOr(SourceFactAndFunc, nullptr); + InterJob; InterJob = InterJob->NextWithSameSourceFactAndCallee) { + + auto CSId = InterJob->CallSite; + auto CallSite = NodeCompressor[CSId]; + auto CallerId = + FunCompressor.getOrInsert(ICFG.getFunctionOf(CallSite)); + + if constexpr (EnableStatistics) { + ++NumInterJobs; + } + propagateProcedureSummaries( + Summaries, CallSite, CSId, Callee, CalleeId, ExitInst, ExitId, + InterJob->SourceFact, CallerId, InterJob->SourceEF); + } + } + + if constexpr (EnableStatistics) { + this->CumulNumInterJobsPerRelevantCall += NumInterJobs; + this->MaxNumInterJobsPerRelevantCall = + std::max(this->MaxNumInterJobsPerRelevantCall, NumInterJobs); + } + } + } + + template > + void submitInitialValues() { + auto Seeds = Problem.initialSeeds(); + for (const auto &[Inst, SeedMap] : Seeds.getSeeds()) { + auto InstId = NodeCompressor.getOrInsert(Inst); + for (const auto &[Fact, Val] : SeedMap) { + auto FactId = FactCompressor.getOrInsert(Fact); + + WLProp.emplace(ValuePropagationJob{InstId, FactId, Val}); + } + } + } + + template > + void propagateValue(uint32_t SPId, uint32_t FactId, l_t Val) { + + /// TODO: Unbalanced return-sites + + auto SP = NodeCompressor[SPId]; + + PHASAR_LOG_LEVEL_CAT(DEBUG, "IterativeIDESolver", + "propagateValue for N: " + << NToString(SP) + << "; D: " << DToString(FactCompressor[FactId]) + << "; L: " << LToString(Val)); + + { + ByConstRef StoredVal = get(SPId, FactId); + auto NewVal = Problem.join(StoredVal, Val); + if (NewVal == StoredVal) { + /// Nothing new, so we have already seen this ValuePropagationJob + /// before. + /// NOTE: We propagate Bottom for the ZeroFact, such that at the + /// first iteration we always get a change here + /// NOTE: Need this early exit for termination in case of recursion + PHASAR_LOG_LEVEL_CAT(DEBUG, "IterativeIDESolver", + "> Value has already been seen!"); + + return; + } + set(SPId, FactId, std::move(NewVal)); + } + + PHASAR_LOG_LEVEL_CAT(DEBUG, "IterativeIDESolver", + "> Make ValuePropagationJob"); + + WLComp.insert(SPId); + + auto Fun = ICFG.getFunctionOf(SP); + + for (ByConstRef CS : ICFG.getCallsFromWithin(Fun)) { + PHASAR_LOG_LEVEL_CAT(DEBUG, "IterativeIDESolver", + "> CS: " << NToString(CS)); + + auto InstId = NodeCompressor.getOrInsert(CS); + + const InterPropagationJob *InterJobs = + SourceFactAndCSToInterJob.getOr(combineIds(FactId, InstId), nullptr); + + for (; InterJobs; InterJobs = InterJobs->NextWithSameSourceFactAndCS) { + auto Callee = FunCompressor[InterJobs->Callee]; + + PHASAR_LOG_LEVEL_CAT(DEBUG, "IterativeIDESolver", + ">> Callee: " << FToString(Callee)); + + for (ByConstRef CalleeSP : ICFG.getStartPointsOf(Callee)) { + auto CalleeSPId = NodeCompressor.getOrInsert(CalleeSP); + + PHASAR_LOG_LEVEL_CAT( + DEBUG, "IterativeIDESolver", + "> emplace { N: " + << NToString(CalleeSP) << "; D: " + << DToString(FactCompressor[InterJobs->FactInCallee]) + << "; L: " + << LToString(InterJobs->SourceEF.computeTarget(Val)) + << " } into WLProp"); + + WLProp.emplace( + ValuePropagationJob{CalleeSPId, InterJobs->FactInCallee, + InterJobs->SourceEF.computeTarget(Val)}); + + if constexpr (EnableStatistics) { + if (WLProp.size() > this->WLPropHighWatermark) { + this->WLPropHighWatermark = WLProp.size(); + } + } + } + } + } + } + + template > + void computeValues(uint32_t SPId) { + auto SP = NodeCompressor[SPId]; + auto Fun = ICFG.getFunctionOf(SP); + + for (ByConstRef Inst : ICFG.getAllInstructionsOf(Fun)) { + if (Inst == SP) { + continue; + } + + if constexpr (EnableJumpFunctionGC == + JumpFunctionGCMode::EnabledAggressively && + has_isInteresting_v) { + if (!Problem.isInteresting(Inst)) { + continue; + } + } + + auto InstId = NodeCompressor.getOrInsert(Inst); + for (auto [SrcTgtFactId, EF] : JumpFunctions[InstId].cells()) { + auto [SrcFactId, TgtFactId] = splitId(SrcTgtFactId); + + ByConstRef Val = get(SPId, SrcFactId); + ByConstRef StoredVal = get(InstId, TgtFactId); + + auto NewVal = Problem.join(StoredVal, EF.computeTarget(std::move(Val))); + if (NewVal != StoredVal) { + set(InstId, TgtFactId, std::move(NewVal)); + } + } + } + } + + llvm::SmallBitVector getCollectableFunctions() { + llvm::SmallVector FunWorkList; + FunWorkList.reserve(CandidateFunctionsForGC.count()); + + llvm::SmallBitVector FinalCandidates(CandidateFunctionsForGC.size()); + + for (auto C : CandidateFunctionsForGC.set_bits()) { + if (RefCountPerFunction[C]) { + auto Fun = FunCompressor[C]; + const auto &Callers = ICFG.getCallersOf(Fun); + for (ByConstRef CS : Callers) { + FunWorkList.push_back( + FunCompressor.getOrInsert(ICFG.getFunctionOf(CS))); + } + } else { + FinalCandidates.set(C); + } + } + + while (!FunWorkList.empty()) { + auto FunId = FunWorkList.pop_back_val(); + + if (!FinalCandidates.test(FunId)) { + continue; + } + + FinalCandidates.reset(FunId); + + auto Fun = FunCompressor[FunId]; + const auto &Callers = ICFG.getCallersOf(Fun); + for (ByConstRef CS : Callers) { + FunWorkList.push_back( + FunCompressor.getOrInsert(ICFG.getFunctionOf(CS))); + } + } + + return FinalCandidates; + } + + void removeJumpFunctionsFor(ByConstRef Inst) { + if (keepAnalysisInformationAt(Inst)) { + return; + } + + size_t InstId = NodeCompressor.getOrInsert(Inst); + + if constexpr (EnableJumpFunctionGC == + JumpFunctionGCMode::EnabledAggressively) { + static_assert( + has_isInteresting_v, + "Aggressive JumpFunctionGC requires the TabulationProblem " + "to define a function 'bool isInteresting(n_t)' that prevents " + "analysis information at that instruction to be removed. " + "Otherwise, the analysis results will be empty!"); + // this->base_results_t::ValTab[InstId].clear(); + } + + if constexpr (EnableStatistics) { + this->NumPathEdges -= JumpFunctions[InstId].size(); + } + + JumpFunctions[InstId].clear(); + } + + void cleanupInterJobsFor(unsigned FunId) { + /// XXX: Use std::erase_if when upgrading to C++20 + + auto Cells = SourceFactAndFuncToInterJob.cells(); + for (auto Iter = Cells.begin(), End = Cells.end(); Iter != End;) { + auto It = Iter++; + auto CalleeId = splitId(It->first).second; + if (CalleeId == FunId) { + SourceFactAndFuncToInterJob.erase(It); + } + } + } + + void collectFunction(unsigned FunId) { + for (ByConstRef Inst : + ICFG.getAllInstructionsOf(FunCompressor[FunId])) { + removeJumpFunctionsFor(Inst); + } + + cleanupInterJobsFor(FunId); + + CandidateFunctionsForGC.reset(FunId); + } + + void runGC() { + llvm::errs() << "runGC() with " << CandidateFunctionsForGC.count() + << " candidates\n"; + + size_t NumCollectedFuns = 0; + + scope_exit FinishGC = [&NumCollectedFuns] { + llvm::errs() << "> Finished GC run (collected " << NumCollectedFuns + << " functions)\n"; + }; + + auto FinalCandidates = getCollectableFunctions(); + + for (auto Candidate : FinalCandidates.set_bits()) { + collectFunction(Candidate); + ++NumCollectedFuns; + } + } + + static constexpr uint64_t combineIds(uint32_t LHS, uint32_t RHS) noexcept { + return (uint64_t(LHS) << 32) | RHS; + } + static constexpr std::pair splitId(uint64_t Id) noexcept { + return {uint32_t(Id >> 32), uint32_t(Id & UINT32_MAX)}; + } + + ProblemTy &Problem; + const i_t &ICFG; + + Compressor FunCompressor{}; + + worklist_t WorkList{NodeCompressor, ICFG}; + + /// --> Begin InterPropagationJobs + + StableVector AllInterPropagationsOwner; + /// Intentionally storing pointers, since we capture them in the containers + /// below + DenseSet + AllInterPropagations{}; + /// Stores keys of the SourceFactAndFuncToInterJob map. All mapped values + /// should be part of the set + DenseSet CallWL{}; + // Stores pointers into AllInterPropagations building up an intrusive + // linked list of all jobs matching the key (CalleeSourceFact x Callee). + // == Reverse Lookup + DenseTable1d + SourceFactAndFuncToInterJob{}; + // Stores pointers into AllInterPropagations building up an intrusive + // linked list of all jobs matching the key (CSSourceFact x CS) + // == Forward Lookup + DenseTable1d + SourceFactAndCSToInterJob{}; + + /// <-- End InterPropagationJobs + + worklist_t WLProp{NodeCompressor, ICFG}; + DenseSet WLComp{}; + + /// Index represents the instruction + llvm::SmallVector JumpFunctions{}; + /// Index represents the function + std::conditional_t, + EmptyType> + EndSummaryTab; + + llvm::OwningArrayRef RefCountPerFunction{}; + llvm::BitVector CandidateFunctionsForGC{}; + + // FlowFunctionCache FFCache{ + // &MRes}; + // EdgeFunctionCache EFCache{&MRes}; + + flow_edge_function_cache_t FECache{Problem}; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_ITERATIVEIDESOLVER_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h new file mode 100644 index 0000000000..9dfa44edff --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h @@ -0,0 +1,165 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERBASE_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERBASE_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" +#include "phasar/Utils/PointerUtils.h" +#include "phasar/Utils/TableWrappers.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" + +#include + +namespace psr { + +template struct IterIDEPropagationJob { + [[no_unique_address]] EdgeFunctionPtrType SourceEF{}; + + uint32_t AtInstruction{}; + uint32_t SourceFact{}; + uint32_t PropagatedFact{}; +}; + +template +class IterativeIDESolverBase { +public: + static constexpr bool ComputeValues = StaticSolverConfigTy::ComputeValues; + static constexpr bool EnableStatistics = + StaticSolverConfigTy::EnableStatistics; + /// NOTE: EdgeFunctionPtrType may be either std::shared_ptr> + /// or llvm::IntrusiveRefCntPtr> once this is supported + using EdgeFunctionPtrType = + std::conditional_t; + +protected: + template + using map_t = typename StaticSolverConfigTy::template map_t; + + template + using set_t = typename StaticSolverConfigTy::template set_t; + + template + using worklist_t = typename StaticSolverConfigTy::template worklist_t; + + template + using flow_edge_function_cache_t = + typename StaticSolverConfigTy::template flow_edge_function_cache_t< + ProblemTy, AutoAddZero>; + + using PropagationJob = IterIDEPropagationJob; + + struct InterPropagationJob { + [[no_unique_address]] EdgeFunctionPtrType SourceEF{}; + + uint32_t SourceFact{}; + uint32_t Callee{}; + + uint32_t CallSite{}; + uint32_t FactInCallee{}; + + /// NOTE: The Next-pointer must be mutable, such that we are able to mutate + /// it from inside a set. Recall that the next pointer does *not* affect + /// equality or hashing + mutable const InterPropagationJob *NextWithSameSourceFactAndCallee{}; + [[no_unique_address]] mutable std::conditional_t< + ComputeValues, const InterPropagationJob *, EmptyType> + NextWithSameSourceFactAndCS{}; + + [[nodiscard]] bool + operator==(const InterPropagationJob &Other) const noexcept { + return SourceFact == Other.SourceFact && Callee == Other.Callee && + CallSite == Other.CallSite && FactInCallee == Other.FactInCallee && + SourceEF == Other.SourceEF; + } + + [[nodiscard]] bool + operator!=(const InterPropagationJob &Other) const noexcept { + return !(*this == Other); + } + + [[nodiscard]] size_t getHashCode() const noexcept { + if constexpr (ComputeValues) { + return llvm::hash_combine(SourceFact, Callee, CallSite, FactInCallee, + SourceEF.getOpaqueValue()); + } else { + return llvm::hash_combine(SourceFact, Callee, CallSite, FactInCallee); + } + } + }; + struct InterPropagationJobRef { + InterPropagationJob *JobPtr{}; + }; + + struct InterPropagationJobRefDSI { + static InterPropagationJobRef getEmptyKey() noexcept { + return {llvm::DenseMapInfo::getEmptyKey()}; + } + static InterPropagationJobRef getTombstoneKey() noexcept { + return {llvm::DenseMapInfo::getTombstoneKey()}; + } + static auto getHashValue(InterPropagationJobRef Job) noexcept { + assert(Job.JobPtr != nullptr); + assert(Job.JobPtr != getEmptyKey().JobPtr); + assert(Job.JobPtr != getTombstoneKey().JobPtr); + return Job.JobPtr->getHashCode(); + } + static bool isEqual(InterPropagationJobRef LHS, + InterPropagationJobRef RHS) noexcept { + if (LHS.JobPtr == RHS.JobPtr) { + return true; + } + if (LHS.JobPtr == getEmptyKey().JobPtr || + LHS.JobPtr == getTombstoneKey().JobPtr || + RHS.JobPtr == getEmptyKey().JobPtr || + RHS.JobPtr == getTombstoneKey().JobPtr) { + return false; + } + + assert(LHS.JobPtr != nullptr); + assert(RHS.JobPtr != nullptr); + return *LHS.JobPtr == *RHS.JobPtr; + } + }; + + union ValueComputationJob { + uint64_t Value{}; + struct { + uint32_t StartPoint; + uint32_t SourceFact; + }; + + ValueComputationJob(uint64_t Value) noexcept : Value(Value) {} + ValueComputationJob(uint32_t SP, uint32_t SF) noexcept + : StartPoint(SP), SourceFact(SF) {} + }; + + struct ValuePropagationJob { + uint32_t Inst{}; + uint32_t Fact{}; + [[no_unique_address]] std::conditional_t< + ComputeValues, typename std::decay_t::l_t, EmptyType> + Value{}; + }; + + struct SummaryEdge { + uint32_t TargetFact{}; + EdgeFunctionPtrType EF{}; + }; + + /// Key is TargetFact + + using SummaryEdges = SmallDenseTable1d; + using SummaryEdges_JF1 = + std::conditional_t, + llvm::SmallDenseSet>>; + + using summaries_t = detail::CellVecSmallVectorTy, + DummyPair>>; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_ITERATIVEIDESOLVERBASE_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h new file mode 100644 index 0000000000..28ba335428 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h @@ -0,0 +1,56 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERRESULTS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERRESULTS_H + +#include "phasar/DataFlow/IfdsIde/Solver/Compressor.h" +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Table.h" +#include "phasar/Utils/TableWrappers.h" + +#include "llvm/ADT/ArrayRef.h" + +#include + +namespace psr::detail { +template class IterativeIDESolverResults { + using inner_map_t = std::conditional_t< + std::is_same_v, + DummyDenseTable1d::id_type>, + DenseTable1d::id_type>>; + +public: + using n_t = N; + using d_t = D; + using l_t = L; + + typename NodeCompressorTraits::type NodeCompressor; + Compressor FactCompressor; + [[no_unique_address]] typename ValCompressorTraits::type ValCompressor; + llvm::SmallVector ValTab; + + using ValTab_value_type = typename decltype(ValTab)::value_type; + + static_assert(std::is_same_v); + + auto ValTab_cellVec() const { + std::vector::Cell> Ret; + Ret.reserve(ValTab.size()); + + for (const auto &[M1, Inst] : llvm::zip(ValTab, NodeCompressor)) { + for (ByConstRef M2 : M1.cells()) { + Ret.emplace_back(Inst, FactCompressor[M2.first], + ValCompressor[M2.second]); + } + } + return Ret; + } +}; + +template +using IterativeIDESolverResults_P = + IterativeIDESolverResults; +} // namespace psr::detail + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_ITERATIVEIDESOLVERRESULTS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverStats.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverStats.h new file mode 100644 index 0000000000..4c705ea745 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverStats.h @@ -0,0 +1,61 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERSTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ITERATIVEIDESOLVERSTATS_H + +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheStats.h" + +#include + +namespace psr { +struct IterativeIDESolverStats { + FlowEdgeFunctionCacheStats FEStats; + + size_t NumAllInterPropagations = 0; + size_t AllInterPropagationsBytes = 0; + size_t SourceFactAndCSToInterJobSize = 0; + size_t SourceFactAndCSToInterJobBytes = 0; + size_t SourceFactAndFuncToInterJobSize = 0; + size_t SourceFactAndFuncToInterJobBytes = 0; + size_t NumPathEdges = 0; + size_t NumPathEdgesHighWatermark = 0; + size_t JumpFunctionsMapBytes = 0; + size_t MaxESGEdgesPerInst = 0; + double AvgESGEdgesPerInst = 0; + size_t CumulESGEdges = 0; + size_t ValTabBytes = 0; + size_t WorkListHighWatermark = 0; + size_t CallWLHighWatermark = 0; + size_t WLPropHighWatermark = 0; + size_t WLCompHighWatermark = 0; + size_t NumFlowFacts = 0; + size_t InstCompressorCapacity = 0; + size_t FactCompressorCapacity = 0; + size_t FunCompressorCapacity = 0; + + size_t TotalNumRelevantCalls = 0; + size_t CumulNumInterJobsPerRelevantCall = 0; + size_t MaxNumInterJobsPerRelevantCall = 0; + size_t TotalNumLinearSearchForSummary = 0; + size_t CumulLinearSearchLenForSummary = 0; + size_t MaxLenLinearSearchForSummary = 0; + size_t CumulDiffNumSummariesFound = 0; + size_t MaxDiffNumSummariesFound = 0; + double CumulRelDiffNumSummariesFound = 0; + + size_t NumEndSummaries = 0; + size_t EndSummaryTabSize = 0; + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const IterativeIDESolverStats &S); +}; + +template +struct SolverStatsSelector {}; + +template +struct SolverStatsSelector< + StaticSolverConfigTy, + std::enable_if_t> + : IterativeIDESolverStats {}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_ITERATIVEIDESOLVERSTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h new file mode 100644 index 0000000000..1dc2316524 --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h @@ -0,0 +1,103 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_STATICIDESOLVERCONFIG_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_STATICIDESOLVERCONFIG_H + +#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCacheNG.h" +#include "phasar/DataFlow/IfdsIde/Solver/WorkListTraits.h" +#include "phasar/Utils/TableWrappers.h" +#include "phasar/Utils/TypeTraits.h" + +#include +#include + +namespace psr { +enum class JumpFunctionGCMode { + /// Perform no semantic garbage collection on jump functions + Disabled, + /// Perform JF GC, but do not delete any elements from the SolverResults, so + /// all SolverResults are still present, only the edge-values are not computed + /// everywhere + Enabled, + /// Perform JF GC as in 'Enabled', but also delete entries from the + /// SolverResults + EnabledAggressively +}; + +struct IDESolverConfigBase { + template + static inline constexpr bool + IsSimple1d = sizeof(std::pair) <= 32 && + std::is_nothrow_move_constructible_v + &&std::is_nothrow_move_constructible_v + &&has_llvm_dense_map_info; + + template + static inline constexpr bool + IsSimpleVal = sizeof(T) <= 32 && std::is_nothrow_move_constructible_v + &&has_llvm_dense_map_info; + + template + using map_t = std::conditional_t, DenseTable1d, + UnorderedTable1d>; + + template + using set_t = + std::conditional_t, DenseSet, UnorderedSet>; + + template using worklist_t = VectorWorkList; + + template + using flow_edge_function_cache_t = + FlowEdgeFunctionCacheNG; + + template using EdgeFunctionPtrType = EdgeFunction; + + static inline constexpr bool AutoAddZero = true; + static inline constexpr bool EnableStatistics = false; + static inline constexpr JumpFunctionGCMode EnableJumpFunctionGC = + JumpFunctionGCMode::Disabled; + static inline constexpr bool UseEndSummaryTab = false; +}; + +template +struct WithComputeValues : Base { + static constexpr bool ComputeValues = ComputeValuesVal; +}; + +template struct WithGCMode : Base { + static constexpr JumpFunctionGCMode EnableJumpFunctionGC = GCMode; +}; + +template struct WithStats : Base { + static constexpr bool EnableStatistics = EnableStats; +}; + +template typename WorkList> +struct WithWorkList : Base { + template using worklist_t = WorkList; +}; + +template struct WithEndSummaryTab : Base { + static inline constexpr bool UseEndSummaryTab = UseEST; +}; + +using IDESolverConfig = WithComputeValues; +using IFDSSolverConfig = WithComputeValues; +using IDESolverConfigWithStats = WithStats; +using IFDSSolverConfigWithStats = WithStats; +using IFDSSolverConfigWithStatsAndGC = + WithGCMode; + +template +struct DefaultIDESolverConfig : IDESolverConfig {}; + +template +struct DefaultIDESolverConfig< + ProblemTy, + std::enable_if_t, + ProblemTy>>> : IFDSSolverConfig {}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_STATICIDESOLVERCONFIG_H diff --git a/include/phasar/DataFlow/IfdsIde/Solver/WorkListTraits.h b/include/phasar/DataFlow/IfdsIde/Solver/WorkListTraits.h new file mode 100644 index 0000000000..560c378ddb --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/Solver/WorkListTraits.h @@ -0,0 +1,122 @@ +#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_WORKLISTTRAITS_H +#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_WORKLISTTRAITS_H + +#include "phasar/DataFlow/IfdsIde/Solver/Compressor.h" + +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Compiler.h" + +#include +#include +#include +#include +namespace psr { + +template class VectorWorkList { +public: + VectorWorkList() noexcept = default; + template + VectorWorkList(const NodeCompressor &NC, const ICFGTy &ICF) noexcept {} + + void reserve(size_t Capacity) { WL.reserve(Capacity); } + + [[nodiscard]] bool empty() const noexcept { return WL.empty(); } + + template void emplace(ArgTys &&...Args) { + WL.emplace_back(std::forward(Args)...); + } + + template + void processEntriesUntilEmpty(HandlerFun Handler) { + while (!WL.empty()) { + auto Item = std::move(WL.back()); + WL.pop_back(); + std::invoke(Handler, std::move(Item)); + } + } + + void clear() noexcept { + WL.clear(); + WL.shrink_to_fit(); + } + + [[nodiscard]] LLVM_DUMP_METHOD size_t size() const noexcept { + return WL.size(); + } + +private: + std::vector WL; +}; + +template class SmallVectorWorkList { + SmallVectorWorkList() noexcept = default; + template + SmallVectorWorkList(const NodeCompressor &NC, const ICFGTy &ICF) noexcept {} + + void reserve(size_t Capacity) { WL.reserve(Capacity); } + + [[nodiscard]] bool empty() const noexcept { return WL.empty(); } + + template void emplace(ArgTys &&...Args) { + WL.emplace_back(std::forward(Args)...); + } + + template + void processEntriesUntilEmpty(HandlerFun Handler) { + while (!WL.empty()) { + auto Item = WL.pop_back_val(); + std::invoke(Handler, std::move(Item)); + } + } + + void clear() noexcept { + /// llvm::SmallVector does not have a shrink_to_fit() function, so use this + /// workaround: + llvm::SmallVector Empty; + swap(Empty, WL); + } + + [[nodiscard]] size_t size() const noexcept { return WL.size(); } + +private: + llvm::SmallVector WL; +}; + +template class DequeWorkList { +public: + DequeWorkList() noexcept = default; + + template + DequeWorkList(const NodeCompressor &NC, const ICFGTy &ICF) noexcept {} + + void reserve(size_t Capacity) { WL.reserve(Capacity); } + + [[nodiscard]] bool empty() const noexcept { return WL.empty(); } + + template void emplace(ArgTys &&...Args) { + WL.emplace_back(std::forward(Args)...); + } + + template + void processEntriesUntilEmpty(HandlerFun Handler) { + while (!WL.empty()) { + auto Item = std::move(WL.front()); + WL.pop_front(); + std::invoke(Handler, std::move(Item)); + } + } + + void clear() noexcept { + WL.clear(); + WL.shrink_to_fit(); + } + + [[nodiscard]] size_t size() const noexcept { return WL.size(); } + +private: + std::deque WL; +}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_WORKLISTTRAITS_H diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 65da6d5e8f..664d0e111d 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -65,6 +65,18 @@ class SolverResultsBase { return self().Results.row(Stmt); } + [[nodiscard]] const auto &rowMapView() const { + return self().Results.rowMapView(); + } + + [[nodiscard]] bool containsNode(ByConstRef Stmt) const { + return self().Results.containsRow(Stmt); + } + + [[nodiscard]] const auto &row(ByConstRef Stmt) const { + return self().Results.row(Stmt); + } + // this function only exists for IFDS problems which use BinaryDomain as their // value domain L template void dumpResults(const ICFGTy &ICF, llvm::raw_ostream &OS = llvm::outs()) const { @@ -180,6 +194,15 @@ class SolverResultsBase { STOP_TIMER("DFA IDE Result Dumping", Full); } + template + void foreachResultEntry(HandlerFn Handler) const { + for (const auto &[Row, RowMap] : rowMapView()) { + for (const auto &[Col, Val] : RowMap) { + std::invoke(Handler, std::make_tuple(Row, Col, Val)); + } + } + } + private: [[nodiscard]] const Derived &self() const noexcept { static_assert(std::is_base_of_v); diff --git a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h index 2e835c6f56..098c4ff217 100644 --- a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h +++ b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h @@ -65,7 +65,7 @@ template class CallStringCTX { friend bool operator<(const CallStringCTX &Lhs, const CallStringCTX &Rhs) { - return Lhs.cs < Rhs.cs; + return Lhs.CallString < Rhs.CallString; } llvm::raw_ostream &print(llvm::raw_ostream &OS) const { diff --git a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h index 4e05c9279d..0be77db3d3 100644 --- a/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/InterMonoSolver.h @@ -24,7 +24,6 @@ #include #include #include -#include namespace psr { @@ -368,18 +367,8 @@ template class InterMonoSolver { } // Compute the data-flow facts using the respective kind of flows if (ICF->isCallSite(Src)) { - // Handle call flow(s) - if (!isIntraEdge(Edge)) { - // real call - for (auto &[Ctx, Facts] : Analysis[Src]) { - processCall(Edge); // TODO: decompose into processCall and - // processCallToRet - } - } else { - // call-to-return - processCall( - Edge); // TODO: decompose into processCall and processCallToRet - } + // Handle call flow(s) and call-to-return flow + processCall(Edge); } else if (ICF->isExitInst(Src)) { // Handle return flow processExit(Edge); diff --git a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h index ac46b90687..739b6fa301 100644 --- a/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h +++ b/include/phasar/DataFlow/Mono/Solver/IntraMonoSolver.h @@ -18,7 +18,9 @@ #define PHASAR_DATAFLOW_MONO_SOLVER_INTRAMONOSOLVER_H #include "phasar/DataFlow/Mono/IntraMonoProblem.h" -#include "phasar/Utils/BitVectorSet.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/DefaultValue.h" +#include "phasar/Utils/Utilities.h" #include #include @@ -109,20 +111,47 @@ template class IntraMonoSolver { } } - mono_container_t getResultsAt(n_t Stmt) { return Analysis[Stmt]; } + [[nodiscard]] const mono_container_t &getResultsAt(n_t Stmt) const { + auto It = Analysis.find(Stmt); + if (It != Analysis.end()) { + return It->second; + } + return getDefaultValue(); + } virtual void dumpResults(llvm::raw_ostream &OS = llvm::outs()) { - OS << "Intra-Monotone solver results:\n" - "------------------------------\n"; - for (auto &[Node, FlowFacts] : this->Analysis) { - OS << "Instruction:\n" << NToString(Node); - OS << "\nFacts:\n"; + OS << "\n***************************************************************\n" + << "* Raw IntraMonoSolver results *\n" + << "***************************************************************\n"; + + if (Analysis.empty()) { + OS << "No results computed!" << '\n'; + return; + } + + std::vector> Cells; + Cells.reserve(Analysis.size()); + Cells.insert(Cells.end(), Analysis.begin(), Analysis.end()); + + std::sort(Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { + if constexpr (std::is_same_v) { + return StringIDLess{}(getMetaDataID(Lhs.first), + getMetaDataID(Rhs.first)); + } else { + // If non-LLVM IR is used + return Lhs.first < Rhs.first; + } + }); + + for (const auto &[Node, FlowFacts] : Cells) { + OS << "Instruction: " << NToString(Node); + OS << "\nFacts: "; if (FlowFacts.empty()) { - OS << "\tEMPTY\n"; + OS << "EMPTY\n"; } else { IMProblem.printContainer(OS, FlowFacts); } - OS << "\n\n"; + OS << "\n"; } } diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h index e1195bb573..3a58bae1e4 100644 --- a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerBase.h @@ -14,8 +14,8 @@ #include "phasar/Utils/Logger.h" #include "phasar/Utils/Utilities.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/IntEqClasses.h" #include "llvm/ADT/SmallVector.h" namespace llvm { diff --git a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h index 6a986d99c7..c92c35aae3 100644 --- a/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h +++ b/include/phasar/DataFlow/PathSensitivity/PathSensitivityManagerMixin.h @@ -26,6 +26,7 @@ #include "llvm/ADT/SetVector.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/raw_ostream.h" #include @@ -33,10 +34,6 @@ #include #include -namespace llvm { -class DbgInfoIntrinsic; -} // namespace llvm - namespace psr { template class PathSensitivityManagerMixin { diff --git a/include/phasar/Domain/LatticeDomain.h b/include/phasar/Domain/LatticeDomain.h index b7b7f15407..d45f7bc45a 100644 --- a/include/phasar/Domain/LatticeDomain.h +++ b/include/phasar/Domain/LatticeDomain.h @@ -280,4 +280,19 @@ struct NonTopBotValue< } // namespace psr +namespace std { +template struct hash> { + size_t operator()(const psr::LatticeDomain &LD) noexcept { + if (LD.isBottom()) { + return SIZE_MAX; + } + if (LD.isTop()) { + return SIZE_MAX - 1; + } + assert(LD.getValueOrNull() != nullptr); + return std::hash{}(*LD.getValueOrNull()); + } +}; +} // namespace std + #endif diff --git a/include/phasar/PhasarLLVM/ControlFlow.h b/include/phasar/PhasarLLVM/ControlFlow.h index 0f4b301359..5ab99e536f 100644 --- a/include/phasar/PhasarLLVM/ControlFlow.h +++ b/include/phasar/PhasarLLVM/ControlFlow.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_CONTROLFLOW_H #define PHASAR_PHASARLLVM_CONTROLFLOW_H +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" diff --git a/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h b/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h new file mode 100644 index 0000000000..165bc20229 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H +#define PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Function.h" + +#include +#include + +namespace psr { +class LLVMProjectIRDB; + +[[nodiscard]] std::vector +getEntryFunctions(const LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints); + +[[nodiscard]] std::vector +getEntryFunctionsMut(LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h new file mode 100644 index 0000000000..348f341f41 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Function.h" + +namespace psr { +class LLVMProjectIRDB; + +class GlobalCtorsDtorsModel { +public: + static constexpr llvm::StringLiteral ModelName = + "__psrCRuntimeGlobalCtorsModel"; + + static constexpr llvm::StringLiteral DtorModelName = + "__psrCRuntimeGlobalDtorsModel"; + + static constexpr llvm::StringLiteral DtorsCallerName = + "__psrGlobalDtorsCaller"; + + static constexpr llvm::StringLiteral UserEntrySelectorName = + "__psrCRuntimeUserEntrySelector"; + + static llvm::Function * + buildModel(LLVMProjectIRDB &IRDB, + llvm::ArrayRef UserEntryPoints); + static llvm::Function * + buildModel(LLVMProjectIRDB &IRDB, + llvm::ArrayRef UserEntryPoints); + + /// Returns true, if a function was generated by phasar. + [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &F) noexcept; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h index 101718a429..c3ac4191b6 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h @@ -44,6 +44,8 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, using CFGBase::print; using ICFGBase::print; + using ICFGBase::printAsJson; + using CFGBase::getAsJson; using ICFGBase::getAsJson; @@ -64,8 +66,10 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, [[nodiscard]] llvm::SmallVector getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; - [[nodiscard]] nlohmann::json getAsJsonImpl() const; + void printAsJsonImpl(llvm::raw_ostream &OS) const; + [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; + [[nodiscard]] size_t getNumCallSitesImpl() const noexcept; llvm::LLVMContext BackwardRetsCtx; llvm::DenseMap BackwardRets; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h new file mode 100644 index 0000000000..05d1c34c30 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H + +#include "phasar/ControlFlow/CallGraph.h" + +namespace llvm { +class Instruction; +class Function; +} // namespace llvm + +namespace psr { +using LLVMBasedCallGraph = + CallGraph; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h new file mode 100644 index 0000000000..1679e7b5cd --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/Utils/Soundness.h" + +namespace psr { +class LLVMProjectIRDB; +enum class CallGraphAnalysisType; +class DIBasedTypeHierarchy; +class LLVMVFTableProvider; +class Resolver; + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(LLVMProjectIRDB &IRDB, CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, + DIBasedTypeHierarchy &TH, LLVMVFTableProvider &VTP, + LLVMAliasInfoRef PT = nullptr, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(const LLVMProjectIRDB &IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(LLVMProjectIRDB &IRDB, CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, + DIBasedTypeHierarchy &TH, LLVMVFTableProvider &VTP, + LLVMAliasInfoRef PT = nullptr, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(const LLVMProjectIRDB &IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S = Soundness::Soundy); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h index 12bc784930..4b9e35dd9e 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h @@ -20,28 +20,26 @@ #include "phasar/ControlFlow/CallGraph.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/ControlFlow/ICFGBase.h" +#include "phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" #include "phasar/Utils/MaybeUniquePtr.h" -#include "phasar/Utils/MemoryResource.h" #include "phasar/Utils/Soundness.h" #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" -#include "nlohmann/json.hpp" - -#include - namespace psr { -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class LLVMProjectIRDB; +class Resolver; class LLVMBasedICFG; template <> struct CFGTraits : CFGTraits {}; @@ -49,20 +47,10 @@ template <> struct CFGTraits : CFGTraits {}; class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { friend ICFGBase; - struct Builder; - public: + // For backward compatibility static constexpr llvm::StringLiteral GlobalCRuntimeModelName = - "__psrCRuntimeGlobalCtorsModel"; - - static constexpr llvm::StringLiteral GlobalCRuntimeDtorModelName = - "__psrCRuntimeGlobalDtorsModel"; - - static constexpr llvm::StringLiteral GlobalCRuntimeDtorsCallerName = - "__psrGlobalDtorsCaller"; - - static constexpr llvm::StringLiteral GlobalCRuntimeUserEntrySelectorName = - "__psrCRuntimeUserEntrySelector"; + GlobalCtorsDtorsModel::ModelName; /// Constructs the ICFG based on the given IRDB and the entry-points using a /// fixpoint iteration. This may take a long time. @@ -83,20 +71,27 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { /// IRDB. True by default explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, CallGraphAnalysisType CGType, llvm::ArrayRef EntryPoints = {}, - LLVMTypeHierarchy *TH = nullptr, + DIBasedTypeHierarchy *TH = nullptr, LLVMAliasInfoRef PT = nullptr, Soundness S = Soundness::Soundy, bool IncludeGlobals = true); + explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints = {}, + Soundness S = Soundness::Soundy, + bool IncludeGlobals = true); + explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, + LLVMVFTableProvider VTP, + llvm::ArrayRef EntryPoints = {}, + 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(CallGraph CG, const LLVMProjectIRDB *IRDB); - explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedCG, - LLVMTypeHierarchy *TH = nullptr); + explicit LLVMBasedICFG(const LLVMProjectIRDB *IRDB, + const CallGraphData &SerializedCG); - // Deleter of LLVMTypeHierarchy may be unknown here... + // Deleter of DIBasedTypeHierarchy may be unknown here... ~LLVMBasedICFG(); LLVMBasedICFG(const LLVMBasedICFG &) = delete; @@ -127,14 +122,19 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { } /// Gets the underlying IRDB - [[nodiscard]] LLVMProjectIRDB *getIRDB() const noexcept { return IRDB; } + [[nodiscard]] const LLVMProjectIRDB *getIRDB() const noexcept { return IRDB; } /// Returns true, if a function was generated by phasar. - [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &) noexcept; + [[nodiscard]] static bool + isPhasarGenerated(const llvm::Function &F) noexcept { + return GlobalCtorsDtorsModel::isPhasarGenerated(F); + } using CFGBase::print; using ICFGBase::print; + using ICFGBase::printAsJson; + using CFGBase::getAsJson; using ICFGBase::getAsJson; @@ -149,19 +149,28 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { [[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 { + void printAsJsonImpl(llvm::raw_ostream &OS) const; + [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; + [[nodiscard]] const LLVMBasedCallGraph &getCallGraphImpl() const noexcept { return CG; } + [[nodiscard]] size_t getNumCallSitesImpl() const noexcept { + return CG.getNumVertexCallSites(); + } + [[nodiscard]] llvm::Function *buildCRuntimeGlobalCtorsDtorsModel( - llvm::Module &M, llvm::ArrayRef UserEntryPoints); + LLVMProjectIRDB &IRDB, llvm::ArrayRef UserEntryPoints); + + void initialize(LLVMProjectIRDB *IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, Soundness S, + bool IncludeGlobals); // --- - CallGraph CG; - LLVMProjectIRDB *IRDB = nullptr; - MaybeUniquePtr TH; + LLVMBasedCallGraph CG; + const LLVMProjectIRDB *IRDB = nullptr; + LLVMVFTableProvider VTP; }; extern template class ICFGBase; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h new file mode 100644 index 0000000000..1fdc8100bf --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h @@ -0,0 +1,40 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMVFTABLEPROVIDER_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMVFTABLEPROVIDER_H + +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" + +#include + +namespace llvm { +class Module; +class DIType; +class GlobalVariable; +} // namespace llvm + +namespace psr { +class LLVMProjectIRDB; + +class LLVMVFTableProvider { +public: + explicit LLVMVFTableProvider(const llvm::Module &Mod); + explicit LLVMVFTableProvider(const LLVMProjectIRDB &IRDB); + + [[nodiscard]] bool hasVFTable(const llvm::DIType *Type) const; + [[nodiscard]] const LLVMVFTable * + getVFTableOrNull(const llvm::DIType *Type) const; + +private: + std::unordered_map TypeVFTMap; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_LLVMVFTABLEPROVIDER_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h index 46c752d3e2..818a59de8d 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h @@ -18,22 +18,34 @@ #define PHASAR_PHASARLLVM_CONTROLFLOW_RESOLVER_CHARESOLVER_H_ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" +#include "phasar/Utils/MaybeUniquePtr.h" namespace llvm { class CallBase; -class Function; } // namespace llvm namespace psr { +class DIBasedTypeHierarchy; class CHAResolver : public Resolver { public: - CHAResolver(LLVMProjectIRDB &IRDB, LLVMTypeHierarchy &TH); + CHAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, + const DIBasedTypeHierarchy *TH); - ~CHAResolver() override = default; + // Deleting an incomplete type (LLVMTypeHierarchy) is UB, so instantiate the + // dtor in CHAResolver.cpp + ~CHAResolver() override; FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } + +protected: + MaybeUniquePtr TH; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h index 3cfd888ca3..3a34ea4f24 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h @@ -19,8 +19,9 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h" #include "phasar/PhasarLLVM/Pointer/TypeGraphs/CachedTypeGraph.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" // To switch the TypeGraph -//#include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" +// #include "phasar/PhasarLLVM/Pointer/TypeGraphs/LazyTypeGraph.h" #include @@ -33,10 +34,27 @@ class BitCastInst; namespace psr { -class DTAResolver : public CHAResolver { +class [[deprecated("Does not work with opaque pointers anymore")]] DTAResolver + : public CHAResolver { public: using TypeGraph_t = CachedTypeGraph; + DTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, + const DIBasedTypeHierarchy *TH); + + ~DTAResolver() override = default; + + FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; + + void otherInst(const llvm::Instruction *Inst) override; + + [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool mutatesHelperAnalysisInformation() + const noexcept override { + return false; + } + protected: TypeGraph_t TypeGraph; @@ -44,26 +62,16 @@ class DTAResolver : public CHAResolver { * An heuristic that return true if the bitcast instruction is interesting to * take into the DTA relational graph */ - static bool - heuristicAntiConstructorThisType(const llvm::BitCastInst *BitCast); + static bool heuristicAntiConstructorThisType( + const llvm::BitCastInst *BitCast); /** * Another heuristic that return true if the bitcast instruction is * interesting to take into the DTA relational graph (use the presence or not * of vtable) + */ bool heuristicAntiConstructorVtablePos(const llvm::BitCastInst *BitCast); - -public: - DTAResolver(LLVMProjectIRDB &IRDB, LLVMTypeHierarchy &TH); - - ~DTAResolver() override = default; - - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; - - void otherInst(const llvm::Instruction *Inst) override; - - [[nodiscard]] std::string str() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h index 1b23477eea..376eb59623 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h @@ -13,39 +13,27 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" namespace llvm { -class Instruction; class CallBase; -class Function; -class StructType; } // namespace llvm namespace psr { class NOResolver final : public Resolver { -protected: - const llvm::Function * - getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, - const llvm::CallBase *CallSite); - public: - NOResolver(LLVMProjectIRDB &IRDB); + NOResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP); ~NOResolver() override = default; - void preCall(const llvm::Instruction *Inst) override; - - void handlePossibleTargets(const llvm::CallBase *CallSite, - FunctionSetTy &PossibleTargets) override; - - void postCall(const llvm::Instruction *Inst) override; - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite) override; - void otherInst(const llvm::Instruction *Inst) override; - [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h index 5755c52072..3bbdc83f5e 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h @@ -17,7 +17,7 @@ #ifndef PHASAR_PHASARLLVM_CONTROLFLOW_RESOLVER_OTFRESOLVER_H_ #define PHASAR_PHASARLLVM_CONTROLFLOW_RESOLVER_OTFRESOLVER_H_ -#include "phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include @@ -26,7 +26,6 @@ #include namespace llvm { -class Instruction; class CallBase; class Function; class Type; @@ -35,27 +34,18 @@ class Value; namespace psr { -class LLVMBasedICFG; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class OTFResolver : public Resolver { -protected: - LLVMBasedICFG &ICF; - LLVMAliasInfoRef PT; - public: - OTFResolver(LLVMProjectIRDB &IRDB, LLVMTypeHierarchy &TH, LLVMBasedICFG &ICF, + OTFResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, LLVMAliasInfoRef PT); ~OTFResolver() override = default; - void preCall(const llvm::Instruction *Inst) override; - void handlePossibleTargets(const llvm::CallBase *CallSite, FunctionSetTy &CalleeTargets) override; - void postCall(const llvm::Instruction *Inst) override; - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite) override; @@ -68,6 +58,14 @@ class OTFResolver : public Resolver { const llvm::Function *CalleeTarget); [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return true; + } + +protected: + LLVMAliasInfoRef PT; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h index 5b67a4c929..f4371c6082 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h @@ -23,15 +23,15 @@ namespace llvm { class CallBase; -class StructType; -class Function; -class StructType; +class DICompositeType; } // namespace llvm namespace psr { +class DIBasedTypeHierarchy; class RTAResolver : public CHAResolver { public: - RTAResolver(LLVMProjectIRDB &IRDB, LLVMTypeHierarchy &TH); + RTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, + const DIBasedTypeHierarchy *TH); ~RTAResolver() override = default; @@ -39,10 +39,15 @@ class RTAResolver : public CHAResolver { [[nodiscard]] std::string str() const override; + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } + private: - void resolveAllocatedStructTypes(); + void resolveAllocatedCompositeTypes(); - std::vector AllocatedStructTypes; + std::vector AllocatedCompositeTypes; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h index 58671342f5..8748e56aa2 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h @@ -20,6 +20,7 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/IR/DerivedTypes.h" #include #include @@ -29,42 +30,62 @@ namespace llvm { class Instruction; class CallBase; class Function; -class StructType; +class DIType; } // namespace llvm namespace psr { class LLVMProjectIRDB; -class LLVMTypeHierarchy; +class LLVMVFTableProvider; +class DIBasedTypeHierarchy; enum class CallGraphAnalysisType; -class LLVMBasedICFG; -class LLVMPointsToInfo; +/// Assuming that `CallSite` is a virtual call through a vtable, retrieves the +/// index in the vtable of the virtual function called. [[nodiscard]] std::optional getVFTIndex(const llvm::CallBase *CallSite); -[[nodiscard]] const llvm::StructType * +/// Assuming that `CallSite` is a call to a non-static member function, +/// retrieves the type of the receiver. Returns nullptr, if the receiver-type +/// could not be extracted +[[nodiscard]] const llvm::DIType * getReceiverType(const llvm::CallBase *CallSite); +/// Assuming that `CallSite` is a virtual call, where `Idx` is retrieved through +/// `getVFTIndex()` and `T` through `getReceiverType()` +[[nodiscard]] const llvm::Function * +getNonPureVirtualVFTEntry(const llvm::DIType *T, unsigned Idx, + const llvm::CallBase *CallSite, + const psr::LLVMVFTableProvider &VTP); + [[nodiscard]] std::string getReceiverTypeName(const llvm::CallBase *CallSite); +/// Checks whether the signature of `DestFun` matches the required withature of +/// `CallSite`, such that `DestFun` qualifies as callee-candidate, if `CallSite` +/// is an indirect/virtual call. [[nodiscard]] bool isConsistentCall(const llvm::CallBase *CallSite, const llvm::Function *DestFun); +[[nodiscard]] bool isVirtualCall(const llvm::Instruction *Inst, + const LLVMVFTableProvider &VTP); + class Resolver { protected: - LLVMProjectIRDB &IRDB; - LLVMTypeHierarchy *TH; - - Resolver(LLVMProjectIRDB &IRDB); + const LLVMProjectIRDB *IRDB; + const LLVMVFTableProvider *VTP; const llvm::Function * - getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, - const llvm::CallBase *CallSite); + getNonPureVirtualVFTEntry(const llvm::DIType *T, unsigned Idx, + const llvm::CallBase *CallSite) { + if (!VTP) { + return nullptr; + } + return psr::getNonPureVirtualVFTEntry(T, Idx, CallSite, *VTP); + } public: using FunctionSetTy = llvm::SmallDenseSet; - Resolver(LLVMProjectIRDB &IRDB, LLVMTypeHierarchy &TH); + Resolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP); virtual ~Resolver() = default; @@ -75,17 +96,28 @@ class Resolver { virtual void postCall(const llvm::Instruction *Inst); - virtual FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) = 0; + [[nodiscard]] FunctionSetTy + resolveIndirectCall(const llvm::CallBase *CallSite); + + [[nodiscard]] virtual FunctionSetTy + resolveVirtualCall(const llvm::CallBase *CallSite) = 0; - virtual FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite); + [[nodiscard]] virtual FunctionSetTy + resolveFunctionPointer(const llvm::CallBase *CallSite); virtual void otherInst(const llvm::Instruction *Inst); [[nodiscard]] virtual std::string str() const = 0; - static std::unique_ptr - create(CallGraphAnalysisType Ty, LLVMProjectIRDB *IRDB, LLVMTypeHierarchy *TH, - LLVMBasedICFG *ICF = nullptr, LLVMAliasInfoRef PT = nullptr); + [[nodiscard]] virtual bool mutatesHelperAnalysisInformation() const noexcept { + // Conservatively returns true. Override if possible + return true; + } + static std::unique_ptr create(CallGraphAnalysisType Ty, + const LLVMProjectIRDB *IRDB, + const LLVMVFTableProvider *VTP, + const DIBasedTypeHierarchy *TH, + LLVMAliasInfoRef PT = nullptr); }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h new file mode 100644 index 0000000000..8645f5c725 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h @@ -0,0 +1,50 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFG_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFG_H + +#include "phasar/ControlFlow/SparseCFGBase.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" + +#include "llvm/ADT/DenseMap.h" + +namespace psr { + +class SparseLLVMBasedCFG; + +template <> struct CFGTraits : CFGTraits { + using v_t = const llvm::Value *; +}; + +class SparseLLVMBasedCFG : public LLVMBasedCFG, + public SparseCFGBase { + friend struct SVFGCache; + friend SparseCFGBase; + +public: + using vgraph_t = + llvm::SmallDenseMap; + + SparseLLVMBasedCFG() noexcept = default; + SparseLLVMBasedCFG( + llvm::SmallDenseMap + &&VGraph) noexcept + : VGraph(std::move(VGraph)) {} + +private: + [[nodiscard]] n_t nextUserOrNullImpl(n_t FromInstruction) const { + return VGraph.lookup(FromInstruction); + } + + vgraph_t VGraph; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFG_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h new file mode 100644 index 0000000000..30e56628d6 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFGPROVIDER_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFGPROVIDER_H + +#include "phasar/ControlFlow/SparseCFGProvider.h" + +namespace llvm { +class Function; +class Value; +} // namespace llvm + +namespace psr { + +template +using SparseLLVMBasedCFGProvider = + SparseCFGProvider; + +[[nodiscard]] constexpr const llvm::Value * +valueOf(const llvm::Value *V) noexcept { + return V; +} + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDCFGPROVIDER_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h new file mode 100644 index 0000000000..2d43ae64ea --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h" + +#include + +namespace psr { +class SparseLLVMBasedCFG; +class DIBasedTypeHierarchy; +struct SVFGCache; + +class SparseLLVMBasedICFG + : public LLVMBasedICFG, + public SparseLLVMBasedCFGProvider { + friend SparseLLVMBasedCFGProvider; + +public: + explicit SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, + CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints = {}, + DIBasedTypeHierarchy *TH = nullptr, + LLVMAliasInfoRef PT = nullptr, + Soundness S = Soundness::Soundy, + bool IncludeGlobals = true); + + /// Creates an ICFG with an already given call-graph + explicit SparseLLVMBasedICFG(CallGraph CG, LLVMProjectIRDB *IRDB, + LLVMAliasInfoRef PT); + + explicit SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, + const nlohmann::json &SerializedCG, + LLVMAliasInfoRef PT); + + ~SparseLLVMBasedICFG(); + +private: + [[nodiscard]] const SparseLLVMBasedCFG & + getSparseCFGImpl(const llvm::Function *Fun, const llvm::Value *Val) const; + + std::unique_ptr SparseCFGCache; + LLVMAliasInfoRef AliasAnalysis; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h new file mode 100644 index 0000000000..927840d252 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h @@ -0,0 +1,74 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H + +#include "phasar/ControlFlow/CallGraph.h" +#include "phasar/ControlFlow/ICFGBase.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFGProvider.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" + +#include + +namespace psr { +class LLVMProjectIRDB; +class LLVMBasedICFG; +class SparseLLVMBasedCFG; +class SparseLLVMBasedICFGView; +struct SVFGCache; + +template <> +struct CFGTraits : CFGTraits {}; + +/// Similar to SparseLLVMBasedICFG; the only difference is that this one *is* no +/// LLVMBasedICFG -- it contains a pointer to an already existing one. +/// It still owns the sparse value-flow graphs +class SparseLLVMBasedICFGView + : public LLVMBasedCFG, + public ICFGBase, + public SparseLLVMBasedCFGProvider { + friend ICFGBase; + friend SparseLLVMBasedCFGProvider; + +public: + explicit SparseLLVMBasedICFGView(const LLVMBasedICFG *ICF, + LLVMAliasInfoRef PT); + + ~SparseLLVMBasedICFGView(); + + // To make the IDESolver happy... + operator const LLVMBasedICFG &() const noexcept { return *ICF; } + +private: + [[nodiscard]] FunctionRange getAllFunctionsImpl() const; + [[nodiscard]] f_t getFunctionImpl(llvm::StringRef Fun) const; + + [[nodiscard]] bool isIndirectFunctionCallImpl(n_t Inst) const; + [[nodiscard]] bool isVirtualFunctionCallImpl(n_t Inst) const; + [[nodiscard]] std::vector allNonCallStartNodesImpl() const; + [[nodiscard]] llvm::SmallVector getCallsFromWithinImpl(f_t Fun) const; + [[nodiscard]] llvm::SmallVector + getReturnSitesOfCallAtImpl(n_t Inst) const; + void printImpl(llvm::raw_ostream &OS) const; + [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; + [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; + + [[nodiscard]] const SparseLLVMBasedCFG & + getSparseCFGImpl(const llvm::Function *Fun, const llvm::Value *Val) const; + + const LLVMBasedICFG *ICF{}; + std::unique_ptr SparseCFGCache; + LLVMAliasInfoRef AliasAnalysis; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_SPARSELLVMBASEDICFG_H diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index 47df476adf..677b26639d 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -18,7 +18,6 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" -#include "llvm/ADT/iterator_range.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" @@ -44,7 +43,8 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// 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); + explicit LLVMProjectIRDB(const llvm::Twine &IRFileName, + bool EnableOpaquePointers = LLVM_VERSION_MAJOR > 14); /// Initializes the new ProjectIRDB with the given IR Module _without_ taking /// ownership. The module is optionally being preprocessed. /// @@ -58,7 +58,8 @@ class LLVMProjectIRDB : public ProjectIRDBBase { /// 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); + explicit LLVMProjectIRDB(llvm::MemoryBufferRef Buf, + bool EnableOpaquePointers = LLVM_VERSION_MAJOR > 14); LLVMProjectIRDB(const LLVMProjectIRDB &) = delete; LLVMProjectIRDB &operator=(LLVMProjectIRDB &) = delete; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h new file mode 100644 index 0000000000..e40d109abe --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h @@ -0,0 +1,76 @@ +#include "phasar/Utils/DefaultValue.h" + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" + +#include +#include +#include +#include + +namespace psr::library_summary { + +struct Parameter { + uint16_t Index{}; +}; + +struct ReturnValue {}; + +struct DataFlowFact { + DataFlowFact(Parameter Param) noexcept : Fact(Param) {} + DataFlowFact(ReturnValue Ret) noexcept : Fact(Ret) {} + + std::variant Fact; +}; + +class FunctionDataFlowFacts { +public: + using ParamaterMappingTy = + std::unordered_map>; + + FunctionDataFlowFacts() noexcept = default; + + // insert a set of data flow facts + void insertSet(llvm::StringRef FuncKey, uint32_t Index, + std::vector OutSet) { + Fdff[FuncKey].try_emplace(Index, std::move(OutSet)); + } + + // insert a single data flow fact + void addElement(llvm::StringRef FuncKey, uint32_t Index, DataFlowFact Out) { + Fdff[FuncKey][Index].emplace_back(Out); + } + + // get outset for a function an the parameter index + [[nodiscard]] const std::vector & + getDataFlowFacts(llvm::StringRef FuncKey, uint32_t Index) const { + auto It = Fdff.find(FuncKey); + if (It != Fdff.end()) { + auto Itt = It->second.find(Index); + return Itt->second; + } + + return getDefaultValue>(); + } + + [[nodiscard]] auto begin() const noexcept { return Fdff.begin(); } + [[nodiscard]] auto end() const noexcept { return Fdff.end(); } + + [[nodiscard]] size_t size() const noexcept { return Fdff.size(); } + [[nodiscard]] bool empty() const noexcept { return size() == 0; } + +private: + [[nodiscard]] const auto & + getDataFlowFactsOrEmpty(llvm::StringRef FuncKey) const { + auto It = Fdff.find(FuncKey); + if (It != Fdff.end()) { + return It->second; + } + + return getDefaultValue(); + } + + llvm::StringMap Fdff; +}; + +} // namespace psr::library_summary diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h deleted file mode 100644 index 016030332b..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/BranchSwitchInstFlowFunction.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class BranchSwitchInstFlowFunction : public FlowFunctionBase { -public: - BranchSwitchInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TStats, - const ExtendedValue &ZeroValue) - : FlowFunctionBase(CurrentInst, TStats, ZeroValue) {} - ~BranchSwitchInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h deleted file mode 100644 index 01aa4b9b4a..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CallToRetFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class CallToRetFlowFunction : public FlowFunctionBase { -public: - CallToRetFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TStats, const ExtendedValue &ZeroValue) - : FlowFunctionBase(CurrentInst, TStats, ZeroValue) {} - ~CallToRetFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h deleted file mode 100644 index a89c93ad19..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/CheckOperandsFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class CheckOperandsFlowFunction : public FlowFunctionBase { -public: - CheckOperandsFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TStats, const ExtendedValue &ZeroValue) - : FlowFunctionBase(CurrentInst, TStats, ZeroValue) {} - ~CheckOperandsFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h deleted file mode 100644 index 8e1e0db4a4..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/FlowFunctionBase.h +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h" - -#include "llvm/IR/Instruction.h" - -namespace psr { - -class FlowFunctionBase : public FlowFunction { -public: - FlowFunctionBase(const llvm::Instruction *CurrentInst, TraceStats &TStats, - const ExtendedValue &ZeroValue) - : CurrentInst(CurrentInst), TStats(TStats), ZeroValue(ZeroValue) {} - ~FlowFunctionBase() override = default; - - std::set computeTargets(ExtendedValue Fact) override; - virtual std::set computeTargetsExt(ExtendedValue &Fact) = 0; - -protected: - const llvm::Instruction *CurrentInst; - TraceStats &TStats; - ExtendedValue ZeroValue; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h deleted file mode 100644 index 0f382d1e48..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GEPInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class GEPInstFlowFunction : public FlowFunctionBase { -public: - GEPInstFlowFunction(const llvm::Instruction *CurrentInst, TraceStats &TStats, - const ExtendedValue &ZeroValue) - : FlowFunctionBase(CurrentInst, TStats, ZeroValue) {} - ~GEPInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h deleted file mode 100644 index e6056f156b..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/GenerateFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class GenerateFlowFunction : public FlowFunctionBase { -public: - GenerateFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~GenerateFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h deleted file mode 100644 index 9f410fd2cc..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/IdentityFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class IdentityFlowFunction : public FlowFunctionBase { -public: - IdentityFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~IdentityFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h deleted file mode 100644 index 245108ce74..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCallee.h +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" - -#include "llvm/IR/AbstractCallSite.h" -#include "llvm/IR/Instruction.h" - -namespace psr { - -class MapTaintedValuesToCallee : public FlowFunction { -public: - MapTaintedValuesToCallee(const llvm::CallInst *CallInst, - const llvm::Function *DestFun, TraceStats &TStats, - const ExtendedValue &ZeroValue) - : CallInst(CallInst), DestFun(DestFun), TStats(TStats), - ZeroValue(ZeroValue) {} - ~MapTaintedValuesToCallee() override = default; - - std::set computeTargets(ExtendedValue Fact) override; - -private: - const llvm::CallInst *CallInst; - const llvm::Function *DestFun; - TraceStats &TStats; - ExtendedValue ZeroValue; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h deleted file mode 100644 index 050cef2739..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MapTaintedValuesToCaller.h +++ /dev/null @@ -1,36 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" - -#include "llvm/IR/Instructions.h" - -namespace psr { - -class MapTaintedValuesToCaller : public FlowFunction { -public: - MapTaintedValuesToCaller(const llvm::CallInst *CallInst, - const llvm::ReturnInst *RetInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : CallInst(CallInst), RetInst(RetInst), TraceStats(TraceStats), ZV(ZV) {} - ~MapTaintedValuesToCaller() override = default; - - std::set computeTargets(ExtendedValue Fact) override; - -private: - const llvm::CallInst *CallInst; - const llvm::ReturnInst *RetInst; - TraceStats &TraceStats; - ExtendedValue ZV; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h deleted file mode 100644 index c0e7ec30b0..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemSetInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class MemSetInstFlowFunction : public FlowFunctionBase { -public: - MemSetInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~MemSetInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h deleted file mode 100644 index d543751556..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/MemTransferInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class MemTransferInstFlowFunction : public FlowFunctionBase { -public: - MemTransferInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~MemTransferInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h deleted file mode 100644 index c5df55522f..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/PHINodeFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class PHINodeFlowFunction : public FlowFunctionBase { -public: - PHINodeFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~PHINodeFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h deleted file mode 100644 index 88785ef911..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/ReturnInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class ReturnInstFlowFunction : public FlowFunctionBase { -public: - ReturnInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~ReturnInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h deleted file mode 100644 index 4ffc14e9d8..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/StoreInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class StoreInstFlowFunction : public FlowFunctionBase { -public: - StoreInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~StoreInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h deleted file mode 100644 index 2707328564..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAEndInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class VAEndInstFlowFunction : public FlowFunctionBase { -public: - VAEndInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~VAEndInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h deleted file mode 100644 index b59b67ae20..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/FlowFunctions/VAStartInstFlowFunction.h +++ /dev/null @@ -1,24 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -namespace psr { - -class VAStartInstFlowFunction : public FlowFunctionBase { -public: - VAStartInstFlowFunction(const llvm::Instruction *CurrentInst, - TraceStats &TraceStats, const ExtendedValue &ZV) - : FlowFunctionBase(CurrentInst, TraceStats, ZV) {} - ~VAStartInstFlowFunction() override = default; - - std::set computeTargetsExt(ExtendedValue &Fact) override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h deleted file mode 100644 index a56be61374..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovRetValWriter.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVRETVALWRITER_H - -#include "TraceStatsWriter.h" - -namespace psr { - -class LcovRetValWriter : public TraceStatsWriter { -public: - LcovRetValWriter(const TraceStats &TStats, const std::string &OutFile) - : TraceStatsWriter(TStats, OutFile) {} - ~LcovRetValWriter() override = default; - - void write() const override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h deleted file mode 100644 index 4b66901020..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LcovWriter.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LCOVWRITER_H - -#include "TraceStatsWriter.h" - -namespace psr { - -class LcovWriter : public TraceStatsWriter { -public: - LcovWriter(const TraceStats &TStats, const std::string &OutFile) - : TraceStatsWriter(TStats, OutFile) {} - ~LcovWriter() override = default; - - void write() const override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h deleted file mode 100644 index 763fc18793..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberEntry.h +++ /dev/null @@ -1,33 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERENTRY_H - -#include - -namespace psr { - -class LineNumberEntry { -public: - LineNumberEntry(unsigned int LineNumber) : LineNumber(LineNumber) {} - ~LineNumberEntry() = default; - - bool operator<(const LineNumberEntry &Rhs) const { - return std::less{}(LineNumber, Rhs.LineNumber); - } - - [[nodiscard]] unsigned int getLineNumber() const { return LineNumber; } - - [[nodiscard]] bool isReturnValue() const { return ReturnVal; } - void setReturnValue(bool ReturnVal) { this->ReturnVal = ReturnVal; } - -private: - unsigned int LineNumber; - bool ReturnVal = false; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h deleted file mode 100644 index d92c5efe3d..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/LineNumberWriter.h +++ /dev/null @@ -1,23 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_LINENUMBERWRITER_H - -#include "TraceStatsWriter.h" - -namespace psr { - -class LineNumberWriter : public TraceStatsWriter { -public: - LineNumberWriter(const TraceStats &TraceStats, const std::string &OutFile) - : TraceStatsWriter(TraceStats, OutFile) {} - ~LineNumberWriter() override = default; - - void write() const override; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h deleted file mode 100644 index 2bdb6d59aa..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h +++ /dev/null @@ -1,44 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_STATS_TRACESTATS_H - -#include "llvm/IR/Instruction.h" - -#include "LineNumberEntry.h" - -#include -#include - -namespace psr { - -class TraceStats { -public: - using FileStats = - std::map>>; - using FunctionStats = std::map>; - using LineNumberStats = std::set; - - TraceStats() = default; - ~TraceStats() = default; - - long add(const llvm::Instruction *Inst, - const std::vector &MemLocationSeq = - std::vector()); - - [[nodiscard]] FileStats getStats() const { return Stats; } - -private: - long add(const llvm::Instruction *Inst, bool IsReturnValue); - - FunctionStats &getFunctionStats(const std::string &File); - LineNumberStats &getLineNumberStats(const std::string &File, - const std::string &FunctionName); - FileStats Stats; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h deleted file mode 100644 index 5ec4eb8ee4..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStatsWriter.h +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -#include -#include - -namespace psr { - -class TraceStatsWriter { -public: - TraceStatsWriter(const TraceStats &TraceStats, std::string OutFile) - : TrStats(TraceStats), OutFile(std::move(OutFile)) {} - virtual ~TraceStatsWriter() = default; - - virtual void write() const = 0; - -protected: - [[nodiscard]] const TraceStats &getTraceStats() const { return TrStats; } - [[nodiscard]] std::string getOutFile() const { return OutFile; } - -private: - const TraceStats &TrStats; - const std::string OutFile; -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h deleted file mode 100644 index 3fa5c74e95..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/DataFlowUtils.h +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" - -#include "llvm/IR/Instructions.h" - -#include -#include -#include -#include - -namespace psr { - -class DataFlowUtils { -public: - DataFlowUtils() = delete; - - static bool isValueTainted(const llvm::Value *CurrentInst, - const ExtendedValue &Fact); - - static bool isMemoryLocationTainted(const llvm::Value *MemLocationMatr, - const ExtendedValue &Fact); - - static std::vector - getMemoryLocationSeqFromMatr(const llvm::Value *MemLocationMatr); - static std::vector - getMemoryLocationSeqFromFact(const ExtendedValue &MemLocationFact); - static std::vector - getVaListMemoryLocationSeqFromFact(const ExtendedValue &VaListFact); - - static bool isMemoryLocationSeqsEqual( - const std::vector &MemLocationSeq1, - const std::vector &MemLocationSeq2); - - static bool isSubsetMemoryLocationSeq( - const std::vector &MemLocationSeqInst, - const std::vector &MemLocationSeqFact); - static std::vector getRelocatableMemoryLocationSeq( - const std::vector &TaintedMemLocationSeq, - const std::vector &SrcMemLocationSeq); - static std::vector joinMemoryLocationSeqs( - const std::vector &MemLocationSeq1, - const std::vector &MemLocationSeq2); - - static bool isPatchableArgumentStore(const llvm::Value *SrcValue, - const ExtendedValue &Fact); - static bool isPatchableArgumentMemcpy( - const llvm::Value *SrcValue, - const std::vector &SrcMemLocationSeq, - const ExtendedValue &Fact); - static bool isPatchableVaListArgument(const llvm::Value *SrcValue, - const ExtendedValue &Fact); - static bool isPatchableReturnValue(const llvm::Value *SrcValue, - const ExtendedValue &Fact); - static std::vector patchMemoryLocationFrame( - const std::vector &PatchableMemLocationSeq, - const std::vector &PatchMemLocationSeq); - - static std::vector< - std::tuple, - const llvm::Value *>> - getSanitizedArgList(const llvm::CallInst *CallInst, - const llvm::Function *DestFun, - const llvm::Value *ZeroValue); - - static const llvm::BasicBlock * - getEndOfTaintedBlock(const llvm::BasicBlock *StartBasicBlock); - static bool removeTaintedBlockInst(const ExtendedValue &Fact, - const llvm::Instruction *CurrentInst); - static bool isAutoGENInTaintedBlock(const llvm::Instruction *CurrentInst); - - static bool isMemoryLocationFact(const ExtendedValue &Ev); - static bool isKillAfterStoreFact(const ExtendedValue &Ev); - static bool isCheckOperandsInst(const llvm::Instruction *CurrentInst); - static bool isAutoIdentity(const llvm::Instruction *CurrentInst, - const ExtendedValue &Fact); - static bool isVarArgParam(const llvm::Value *Param, - const llvm::Value *ZeroValue); - static bool isVaListType(const llvm::Type *Type); - static bool isReturnValue(const llvm::Instruction *CurrentInst, - const llvm::Instruction *SuccessorInst); - static bool isArrayDecay(const llvm::Value *MemLocationMatr); - static bool isGlobalMemoryLocationSeq( - const std::vector &MemLocationSeq); - - static void dumpFact(const ExtendedValue &Ev); - - static std::set getTaintedFunctions(); - static std::set getBlacklistedFunctions(); - - static std::string getTraceFilenamePrefix(const std::string &EntryPoint); -}; - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h deleted file mode 100644 index 01ff032464..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/ExtendedValue.h +++ /dev/null @@ -1,203 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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 -#include -#include - -namespace llvm { - -class Value; -} // namespace llvm - -namespace psr { - -class ExtendedValue { -public: - ExtendedValue() = default; - explicit ExtendedValue(const llvm::Value *Val) : Val(Val) { - assert(Val && "ExtendedValue requires an llvm::Value* object"); - } - ~ExtendedValue() = default; - - bool operator==(const ExtendedValue &Rhs) const { - bool IsValueEqual = Val == Rhs.Val; - if (!IsValueEqual) { - return false; - } - - bool IsMemLocationSeqEqual = MemLocationSeq == Rhs.MemLocationSeq; - if (!IsMemLocationSeqEqual) { - return false; - } - - bool IsEndOfTaintedBlockLabelEqual = - EndOfTaintedBlockLabel == Rhs.EndOfTaintedBlockLabel; - if (!IsEndOfTaintedBlockLabelEqual) { - return false; - } - - bool IsVaListMemLocationSeqEqual = - VaListMemLocationSeq == Rhs.VaListMemLocationSeq; - if (!IsVaListMemLocationSeqEqual) { - return false; - } - - bool IsVarArgIndexEqual = VarArgIndex == Rhs.VarArgIndex; - if (!IsVarArgIndexEqual) { - return false; - } - - bool IsCurrentVarArgIndexEqual = - CurrentVarArgIndex == Rhs.CurrentVarArgIndex; - if (!IsCurrentVarArgIndexEqual) { - return false; - } - - return true; - } - - bool operator<(const ExtendedValue &Rhs) const { - if (std::less{}(Val, Rhs.Val)) { - return true; - } - if (std::less{}(Rhs.Val, Val)) { - return false; - } - - if (MemLocationSeq < Rhs.MemLocationSeq) { - return true; - } - if (Rhs.MemLocationSeq < MemLocationSeq) { - return false; - } - - if (std::less{}(EndOfTaintedBlockLabel, - Rhs.EndOfTaintedBlockLabel)) { - return true; - } - if (std::less{}(Rhs.EndOfTaintedBlockLabel, - EndOfTaintedBlockLabel)) { - return false; - } - - if (VaListMemLocationSeq < Rhs.VaListMemLocationSeq) { - return true; - } - if (Rhs.VaListMemLocationSeq < VaListMemLocationSeq) { - return false; - } - - if (std::less{}(VarArgIndex, Rhs.VarArgIndex)) { - return true; - } - if (std::less{}(Rhs.VarArgIndex, VarArgIndex)) { - return false; - } - - return std::less{}(CurrentVarArgIndex, Rhs.CurrentVarArgIndex); - } - - [[nodiscard]] const llvm::Value *getValue() const { return Val; } - - [[nodiscard]] std::vector getMemLocationSeq() const { - return MemLocationSeq; - } - void - setMemLocationSeq(const std::vector &MemLocationSeq) { - this->MemLocationSeq = MemLocationSeq; - } - - [[nodiscard]] std::string getEndOfTaintedBlockLabel() const { - return EndOfTaintedBlockLabel; - } - void setEndOfTaintedBlockLabel(const std::string &EndOfTaintedBlockLabel) { - this->EndOfTaintedBlockLabel = EndOfTaintedBlockLabel; - } - - [[nodiscard]] std::vector - getVaListMemLocationSeq() const { - return VaListMemLocationSeq; - } - void setVaListMemLocationSeq( - const std::vector &VaListMemLocationSeq) { - this->VaListMemLocationSeq = VaListMemLocationSeq; - } - - [[nodiscard]] long getVarArgIndex() const { return VarArgIndex; } - void setVarArgIndex(long VarArgIndex) { this->VarArgIndex = VarArgIndex; } - - void resetVarArgIndex() { - if (!isVarArgTemplate()) { - VarArgIndex = -1L; - } - } - - [[nodiscard]] long getCurrentVarArgIndex() const { - return CurrentVarArgIndex; - } - void incrementCurrentVarArgIndex() { - if (!isVarArgTemplate()) { - ++CurrentVarArgIndex; - } - } - - [[nodiscard]] bool isVarArg() const { return VarArgIndex > -1L; } - [[nodiscard]] bool isVarArgTemplate() const { - return VaListMemLocationSeq.empty() && isVarArg(); - } - -private: - const llvm::Value *Val = nullptr; - std::vector MemLocationSeq; - std::string EndOfTaintedBlockLabel; - - std::vector VaListMemLocationSeq; - long VarArgIndex = -1L; - long CurrentVarArgIndex = -1L; -}; - -} // namespace psr - -namespace std { - -template <> struct hash { - std::size_t operator()(const psr::ExtendedValue &Ev) const { - std::size_t Seed = 0x4711; - - Seed ^= hash{}(Ev.getValue()) + 0x9e3779b9 + - (Seed << 6) + (Seed >> 2); - - for (const auto &MemLocationPart : Ev.getMemLocationSeq()) { - Seed ^= hash{}(MemLocationPart) + 0x9e3779b9 + - (Seed << 6) + (Seed >> 2); - } - - Seed ^= hash{}(Ev.getEndOfTaintedBlockLabel()) + 0x9e3779b9 + - (Seed << 6) + (Seed >> 2); - - for (const auto &VaListMemLocationPart : Ev.getVaListMemLocationSeq()) { - Seed ^= hash{}(VaListMemLocationPart) + 0x9e3779b9 + - (Seed << 6) + (Seed >> 2); - } - - Seed ^= hash{}(Ev.getVarArgIndex()) + 0x9e3779b9 + (Seed << 6) + - (Seed >> 2); - - Seed ^= hash{}(Ev.getCurrentVarArgIndex()) + 0x9e3779b9 + - (Seed << 6) + (Seed >> 2); - - return Seed; - } -}; - -} // namespace std - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h deleted file mode 100644 index 29b3835b39..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Utils/Log.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H -#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_IFDSFIELDSENSTAINTANALYSIS_UTILS_LOG_H - -#include "llvm/Support/raw_ostream.h" - -#define LOG_INFO(x) \ - do { \ - llvm::outs() << "[ENV_TRACE] " << x << "\n"; /*NOLINT*/ \ - \ - llvm::outs().flush(); \ - } while (0) - -#ifdef DEBUG_BUILD -#define LOG_DEBUG(x) LOG_INFO(x) -#else -#define LOG_DEBUG(x) \ - do { \ - } while (0) -#endif - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h index e994671bbe..cf568e2837 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h @@ -13,7 +13,6 @@ #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/IR/Constant.h" @@ -30,7 +29,6 @@ #include #include #include -#include namespace psr { diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h new file mode 100644 index 0000000000..19e03b49bf --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h @@ -0,0 +1,74 @@ +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/FunctionDataFlowFacts.h" +#include "phasar/Utils/DefaultValue.h" + +#include "llvm/IR/Argument.h" +#include "llvm/IR/Function.h" + +#include +#include + +namespace psr::library_summary { + +class LLVMFunctionDataFlowFacts; +[[nodiscard]] LLVMFunctionDataFlowFacts +readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); + +class LLVMFunctionDataFlowFacts { +public: + LLVMFunctionDataFlowFacts() noexcept = default; + using ParamaterMappingTy = FunctionDataFlowFacts::ParamaterMappingTy; + + /// insert a set of data flow facts + void insertSet(const llvm::Function *Fun, uint32_t Index, + std::vector OutSet) { + + LLVMFdff[Fun].try_emplace(Index, std::move(OutSet)); + } + void insertSet(const llvm::Function *Fun, const llvm::Argument *Arg, + std::vector OutSet) { + + insertSet(Fun, Arg->getArgNo(), std::move(OutSet)); + } + + void addElement(const llvm::Function *Fun, uint32_t Index, DataFlowFact Out) { + LLVMFdff[Fun][Index].emplace_back(Out); + } + void addElement(const llvm::Function *Fun, const llvm::Argument *Arg, + DataFlowFact Out) { + addElement(Fun, Arg->getArgNo(), Out); + } + + [[nodiscard]] bool contains(const llvm::Function *Fn) { + return LLVMFdff.count(Fn); + } + + [[nodiscard]] const std::vector & + getFacts(const llvm::Function *Fun, uint32_t Index) { + auto Iter = LLVMFdff.find(Fun); + if (Iter != LLVMFdff.end()) { + return Iter->second[Index]; + } + return getDefaultValue>(); + } + [[nodiscard]] const std::vector & + getFacts(const llvm::Function *Fun, const llvm::Argument *Arg) { + return getFacts(Fun, Arg->getArgNo()); + } + + [[nodiscard]] const ParamaterMappingTy & + getFactsForFunction(const llvm::Function *Fun) { + auto Iter = LLVMFdff.find(Fun); + if (Iter != LLVMFdff.end()) { + return Iter->second; + } + return getDefaultValue(); + } + + friend LLVMFunctionDataFlowFacts + readFromFDFF(const FunctionDataFlowFacts &Fdff, const LLVMProjectIRDB &Irdb); + +private: + std::unordered_map LLVMFdff; +}; +} // namespace psr::library_summary diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h index 4d17597b70..b4f7676f6f 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h @@ -11,6 +11,7 @@ #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMSOLVERRESULTS_H #include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h new file mode 100644 index 0000000000..cea1c2772c --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LibCSummary.h @@ -0,0 +1,9 @@ +#pragma once + +namespace psr { +namespace library_summary { +class FunctionDataFlowFacts; +} // namespace library_summary + +[[nodiscard]] const library_summary::FunctionDataFlowFacts &getLibCSummary(); +} // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h index e7dceb8eb6..34012ec334 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEExtendedTaintAnalysis.h @@ -42,7 +42,6 @@ namespace psr { class LLVMBasedICFG; -template class SolverResults; struct IDEExtendedTaintAnalysisDomain : public LLVMAnalysisDomainDefault { using d_t = AbstractMemoryLocation; @@ -170,7 +169,7 @@ class IDEExtendedTaintAnalysis SourceConfigTy &&SourceConfig, SinkConfigTy &&SinkConfig); - void doPostProcessing(const SolverResults &SR); + void doPostProcessing(GenericSolverResults SR); public: /// Constructor. If EntryPoints is empty, use the TaintAPI functions as @@ -250,7 +249,7 @@ class IDEExtendedTaintAnalysis // Printing functions - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS = llvm::outs()) override; private: @@ -289,13 +288,13 @@ class IDEExtendedTaintAnalysis /// may not be sanitized. /// /// This function involves a post-processing step the first time it is called. - const LeakMap_t &getAllLeaks(const SolverResults &SR) &; + const LeakMap_t &getAllLeaks(GenericSolverResults SR) &; /// Return a map from llvm::Instruction to sets of leaks (llvm::Values) that /// may not be sanitized. /// /// This function involves a post-processing step the first time it is called. - LeakMap_t getAllLeaks(const SolverResults &SR) &&; + LeakMap_t getAllLeaks(GenericSolverResults SR) &&; /// Return a map from llvm::Instruction to sets of leaks (llvm::Values) that /// may or may not be sanitized. /// diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h new file mode 100644 index 0000000000..9cca9dc15e --- /dev/null +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h @@ -0,0 +1,377 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEFEATURETAINTANALYSIS_H +#define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEFEATURETAINTANALYSIS_H + +#include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" +#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/Utils/BitVectorSet.h" +#include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/FunctionExtras.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallBitVector.h" + +#include +#include +#include +#include +#include + +namespace llvm { +class GlobalVariable; +} // namespace llvm + +namespace psr { +class LLVMProjectIRDB; + +struct IDEFeatureTaintEdgeFact { + llvm::SmallBitVector Taints; + + [[nodiscard]] static llvm::SmallBitVector fromBits(uintptr_t Bits) { +#if __has_builtin(__builtin_constant_p) + if (__builtin_constant_p(Bits) && Bits == 0) { + return {}; + } +#endif + + llvm::SmallBitVector Ret(llvm::findLastSet(Bits) + 1); + Ret.setBitsInMask((const uint32_t *)&Bits, sizeof(Bits)); + return Ret; + } + + IDEFeatureTaintEdgeFact(llvm::SmallBitVector &&Taints) noexcept + : Taints(std::move(Taints)) {} + IDEFeatureTaintEdgeFact(const llvm::SmallBitVector &Taints) + : Taints(Taints) {} + IDEFeatureTaintEdgeFact(uintptr_t Taints) noexcept + : Taints(fromBits(Taints)) {} + explicit IDEFeatureTaintEdgeFact() noexcept { Taints.invalid(); } + + void unionWith(uintptr_t Facts) { + auto RequiredSize = llvm::findLastSet(Facts) + 1; + if (RequiredSize > Taints.size()) { + Taints.resize(RequiredSize); + } + Taints.setBitsInMask((const uint32_t *)&Facts, + sizeof(Facts) / sizeof(uint32_t)); + } + void unionWith(const IDEFeatureTaintEdgeFact &Facts) { + if (Facts.isTop()) { + return; + } + Taints |= Facts.Taints; + } + + /// Checks whether this set contains no facts. + /// Note: Top also counts as empty + [[nodiscard]] inline bool empty() const noexcept { + return isTop() || Taints.none(); + } + /// Returns the number of facts in this set. + /// Note: Top counts as empty + [[nodiscard]] inline size_t size() const noexcept { + if (isTop()) { + return 0; + } + return Taints.count(); + } + + [[nodiscard]] inline bool isBottom() const noexcept { return Taints.empty(); } + [[nodiscard]] inline bool isTop() const noexcept { + return Taints.isInvalid(); + } + + [[nodiscard]] friend llvm::hash_code + hash_value(const IDEFeatureTaintEdgeFact &BV) noexcept { + if (BV.Taints.empty()) { + return {}; + } + uintptr_t Buf; + auto Words = BV.Taints.getData(Buf); + size_t Idx = Words.size(); + while (Idx && Words[Idx - 1] == 0) { + --Idx; + } + return llvm::hash_combine_range(Words.begin(), + std::next(Words.begin(), Idx)); + } + + friend bool operator==(const IDEFeatureTaintEdgeFact &Lhs, + const IDEFeatureTaintEdgeFact &Rhs) noexcept { + bool LeftEmpty = Lhs.Taints.none(); + bool RightEmpty = Rhs.Taints.none(); + if (LeftEmpty || RightEmpty) { + return LeftEmpty == RightEmpty; + } + // Check, whether Lhs and Rhs actually have the same bits set and not + // whether their internal representation is exactly identitcal + + uintptr_t LBuf, RBuf; + auto LhsWords = Lhs.Taints.getData(LBuf); + auto RhsWords = Rhs.Taints.getData(RBuf); + if (LhsWords.size() == RhsWords.size()) { + return LhsWords == RhsWords; + } + auto MinSize = std::min(LhsWords.size(), RhsWords.size()); + if (LhsWords.slice(0, MinSize) != RhsWords.slice(0, MinSize)) { + return false; + } + auto Rest = (LhsWords.size() > RhsWords.size() ? LhsWords : RhsWords) + .slice(MinSize); + return std::all_of(Rest.begin(), Rest.end(), + [](auto Word) { return Word == 0; }); + } + + friend bool operator!=(const IDEFeatureTaintEdgeFact &Lhs, + const IDEFeatureTaintEdgeFact &Rhs) noexcept { + return !(Lhs == Rhs); + } + + template [[nodiscard]] std::string str() const { + auto BV = BitVectorSet::fromBits(Taints); + return LToString(BV); + } + + template [[nodiscard]] auto toBVSet() const { + if (isTop()) { + return BitVectorSet(); + } + return BitVectorSet::fromBits(Taints); + } + + template [[nodiscard]] auto toSet() const { + std::set Ret; + if (isTop()) { + return Ret; + } + + for (const auto &Elem : this->template toBVSet()) { + Ret.insert(Elem); + } + return Ret; + } +}; + +[[nodiscard]] std::string LToString(const IDEFeatureTaintEdgeFact &EdgeFact); + +template <> struct JoinLatticeTraits { + inline static IDEFeatureTaintEdgeFact top() { + IDEFeatureTaintEdgeFact Ret{}; + return Ret; + } + inline static IDEFeatureTaintEdgeFact bottom() { + // TODO + return 0; + } + inline static IDEFeatureTaintEdgeFact join(const IDEFeatureTaintEdgeFact &L, + const IDEFeatureTaintEdgeFact &R) { + if (L.isTop()) { + return R; + } + if (R.isTop()) { + return L; + } + auto Ret = L; + Ret.Taints |= R.Taints; + return Ret; + } +}; + +struct IDEFeatureTaintAnalysisDomain : LLVMAnalysisDomainDefault { + using l_t = IDEFeatureTaintEdgeFact; +}; + +class FeatureTaintGenerator { +public: + using InstOrGlobal = + std::variant; + + using GenerateTaintsFn = + llvm::unique_function; + using IsSourceFn = llvm::unique_function; + using PrinterFn = + llvm::unique_function; + + template + static GenerateTaintsFn createGenerateTaints(EdgeFactGenerator &&EFGen) { + return [EFGen{std::forward(EFGen)}](InstOrGlobal IG) { + const auto &TaintSet = std::invoke(EFGen, IG); + BitVectorSet, llvm::SmallBitVector> BV( + llvm::adl_begin(TaintSet), llvm::adl_end(TaintSet)); + + return IDEFeatureTaintEdgeFact{std::move(BV).getBits()}; + }; + } + + template + static PrinterFn createEdgeFactPrinter() { + using ElemTy = + ElementType>; + return [](const IDEFeatureTaintEdgeFact &Fact) { + auto BV = + BitVectorSet::fromBits(Fact.Taints); + return LToString(BV); + }; + } + + FeatureTaintGenerator(IsSourceFn IsFeatureSource, + GenerateTaintsFn GenerateTaints, PrinterFn Printer) + : IsFeatureSource(std::move(IsFeatureSource)), + GenerateTaints(std::move(GenerateTaints)), Printer(std::move(Printer)) { + } + + template + FeatureTaintGenerator(SourceDetector &&SrcDetector, EdgeFactGenerator &&EFGen) + : IsFeatureSource(std::forward(SrcDetector)), + GenerateTaints( + createGenerateTaints(std::forward(EFGen))), + Printer(createEdgeFactPrinter()) {} + + template >>> + FeatureTaintGenerator(EdgeFactGenerator &&EFGen) + : FeatureTaintGenerator( + [EFGen](InstOrGlobal IG) { + return !llvm::empty(std::invoke(EFGen, IG)); + }, + std::forward(EFGen)) {} + + [[nodiscard]] bool isSource(InstOrGlobal IG) const { + return IsFeatureSource(IG); + } + + [[nodiscard]] IDEFeatureTaintEdgeFact + getGeneratedTaintsAt(InstOrGlobal IG) const { + return GenerateTaints(IG); + } + + [[nodiscard]] std::string + toString(const IDEFeatureTaintEdgeFact &Fact) const { + return Printer(Fact); + } + +private: + IsSourceFn IsFeatureSource; + GenerateTaintsFn GenerateTaints; + PrinterFn Printer; +}; + +class IDEFeatureTaintAnalysis + : public IDETabulationProblem { + +public: + IDEFeatureTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, + std::vector EntryPoints, + FeatureTaintGenerator &&TaintGen); + + template + IDEFeatureTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, + std::vector EntryPoints, + EdgeFactGenerator &&EFGen) + : IDEFeatureTaintAnalysis( + IRDB, PT, std::move(EntryPoints), + FeatureTaintGenerator(std::forward(EFGen))) {} + + template + IDEFeatureTaintAnalysis(const LLVMProjectIRDB *IRDB, LLVMAliasInfoRef PT, + std::vector EntryPoints, + SourceDetector &&SrcDetector, + EdgeFactGenerator &&EFGen) + : IDEFeatureTaintAnalysis( + IRDB, PT, std::move(EntryPoints), + FeatureTaintGenerator(std::forward(SrcDetector), + std::forward(EFGen))) {} + + IDEFeatureTaintAnalysis(const IDEFeatureTaintAnalysis &) = delete; + IDEFeatureTaintAnalysis &operator=(const IDEFeatureTaintAnalysis &) = delete; + + IDEFeatureTaintAnalysis(IDEFeatureTaintAnalysis &&) noexcept = default; + IDEFeatureTaintAnalysis & + operator=(IDEFeatureTaintAnalysis &&) noexcept = delete; + + // The EF Caches are incomplete, so move the dtor into the .cpp + ~IDEFeatureTaintAnalysis() override; + + ////////////////////////////////////////////////////////////////////////////// + /// Flow Functions + ////////////////////////////////////////////////////////////////////////////// + + FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override; + + FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override; + + FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, + n_t ExitInst, n_t RetSite) override; + FlowFunctionPtrType + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) override; + FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, + f_t DestFun) override; + + ////////////////////////////////////////////////////////////////////////////// + /// Edge Functions + ////////////////////////////////////////////////////////////////////////////// + + EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, + d_t SuccNode) override; + + EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t DestinationFunction, + d_t DestNode) override; + + EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, + n_t ExitStmt, d_t ExitNode, + n_t RetSite, d_t RetNode) override; + + EdgeFunction + getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, + d_t RetSiteNode, + llvm::ArrayRef Callees) override; + + EdgeFunction getSummaryEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, + d_t SuccNode) override; + + ////////////////////////////////////////////////////////////////////////////// + /// Misc + ////////////////////////////////////////////////////////////////////////////// + + InitialSeeds initialSeeds() override; + + bool isZeroValue(d_t FlowFact) const noexcept override; + + void emitTextReport(GenericSolverResults SR, + llvm::raw_ostream &OS = llvm::outs()) override; + + EdgeFunction extend(const EdgeFunction &FirstEF, + const EdgeFunction &SecondEF) override; + EdgeFunction combine(const EdgeFunction &FirstEF, + const EdgeFunction &OtherEF) override; + +private: + FeatureTaintGenerator TaintGen; + LLVMAliasInfoRef PT; + + struct GenerateEF; + struct AddFactsEF; + DefaultEdgeFunctionSingletonCacheImpl GenEFCache; + DefaultEdgeFunctionSingletonCacheImpl AddEFCache; +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEFEATURETAINTANALYSIS_H diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h index c26a6191ce..783698c91c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/EdgeValue.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEGENERALIZEDLCA_EDGEVALUE_H +#include "llvm/ADT/APFloat.h" #include "llvm/ADT/APSInt.h" #include "llvm/ADT/Twine.h" #include "llvm/IR/Constant.h" diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h index edde5b462f..0b1de3088b 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h @@ -108,10 +108,10 @@ class IDEGeneralizedLCA : public IDETabulationProblem { // void printIDEReport(llvm::raw_ostream &OS, // SolverResults &SR) override; - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS) override; - lca_results_t getLCAResults(SolverResults SR); + lca_results_t getLCAResults(GenericSolverResults SR); private: const LLVMBasedICFG *ICF{}; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h index 40d1dff446..4bfe3ecccd 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h @@ -11,11 +11,13 @@ #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEINSTINTERACTIONANALYSIS_H #include "phasar/DataFlow/IfdsIde/DefaultEdgeFunctionSingletonCache.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Domain/LatticeDomain.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMSolverResults.h" @@ -28,6 +30,7 @@ #include "phasar/Utils/BitVectorSet.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Twine.h" @@ -949,59 +952,15 @@ class IDEInstInteractionAnalysisT l_t computeTarget(ByConstRef /* Src */) const { return Replacement; } - static EdgeFunction compose(EdgeFunctionRef This, - const EdgeFunction SecondFunction) { - - if (auto Default = defaultComposeOrNull(This, SecondFunction)) { - return Default; - } - - auto Cache = This.getCacheOrNull(); - assert(Cache != nullptr && "We expect a cache, because " - "IIAAKillOrReplaceEF is too large for SOO"); - - if (auto *AD = llvm::dyn_cast(SecondFunction)) { - auto Union = - IDEInstInteractionAnalysisT::joinImpl(This->Replacement, AD->Data); - return Cache->createEdgeFunction(std::move(Union)); - } - - if (auto *KR = llvm::dyn_cast(SecondFunction)) { - return SecondFunction; - } - llvm::report_fatal_error( - "found unexpected edge function in 'IIAAKillOrReplaceEF'"); + static EdgeFunction + compose(EdgeFunctionRef /*This*/, + const EdgeFunction /*SecondFunction*/) { + llvm::report_fatal_error("Implemented in 'extend'"); } - static EdgeFunction join(EdgeFunctionRef This, - const EdgeFunction &OtherFunction) { - /// XXX: Here, we underapproximate joins with EdgeIdentity - if (llvm::isa>(OtherFunction)) { - return This; - } - - if (auto Default = defaultJoinOrNull(This, OtherFunction)) { - return Default; - } - - auto Cache = This.getCacheOrNull(); - assert(Cache != nullptr && "We expect a cache, because " - "IIAAKillOrReplaceEF is too large for SOO"); - - if (auto *AD = llvm::dyn_cast(OtherFunction)) { - auto ADCache = OtherFunction.template getCacheOrNull(); - assert(ADCache); - auto Union = - IDEInstInteractionAnalysisT::joinImpl(This->Replacement, AD->Data); - return ADCache->createEdgeFunction(std::move(Union)); - } - if (auto *KR = llvm::dyn_cast(OtherFunction)) { - auto Union = IDEInstInteractionAnalysisT::joinImpl(This->Replacement, - KR->Replacement); - return Cache->createEdgeFunction(std::move(Union)); - } - llvm::report_fatal_error( - "found unexpected edge function in 'IIAAKillOrReplaceEF'"); + static EdgeFunction join(EdgeFunctionRef /*This*/, + const EdgeFunction & /*OtherFunction*/) { + llvm::report_fatal_error("Implemented in 'combine'"); } bool operator==(const IIAAKillOrReplaceEF &Other) const noexcept { @@ -1044,55 +1003,15 @@ class IDEInstInteractionAnalysisT return IDEInstInteractionAnalysisT::joinImpl(Src, Data); } - static EdgeFunction compose(EdgeFunctionRef This, - const EdgeFunction &SecondFunction) { - if (auto Default = defaultComposeOrNull(This, SecondFunction)) { - return Default; - } - - auto Cache = This.getCacheOrNull(); - assert(Cache != nullptr && "We expect a cache, because " - "IIAAAddLabelsEF is too large for SOO"); - - if (auto *AD = llvm::dyn_cast(SecondFunction)) { - auto Union = - IDEInstInteractionAnalysisT::joinImpl(This->Data, AD->Data); - return Cache->createEdgeFunction(std::move(Union)); - } - if (auto *KR = llvm::dyn_cast(SecondFunction)) { - return SecondFunction; - } - llvm::report_fatal_error( - "found unexpected edge function in 'IIAAAddLabelsEF'"); + static EdgeFunction + compose(EdgeFunctionRef /*This*/, + const EdgeFunction & /*SecondFunction*/) { + llvm::report_fatal_error("Implemented in 'extend'"); } - static EdgeFunction join(EdgeFunctionRef This, - const EdgeFunction &OtherFunction) { - /// XXX: Here, we underapproximate joins with EdgeIdentity - if (llvm::isa>(OtherFunction)) { - return This; - } - - if (auto Default = defaultJoinOrNull(This, OtherFunction)) { - return Default; - } - - auto Cache = This.getCacheOrNull(); - assert(Cache != nullptr && "We expect a cache, because " - "IIAAAddLabelsEF is too large for SOO"); - - if (auto *AD = llvm::dyn_cast(OtherFunction)) { - auto Union = - IDEInstInteractionAnalysisT::joinImpl(This->Data, AD->Data); - return Cache->createEdgeFunction(std::move(Union)); - } - if (auto *KR = llvm::dyn_cast(OtherFunction)) { - auto Union = - IDEInstInteractionAnalysisT::joinImpl(This->Data, KR->Replacement); - return Cache->createEdgeFunction(std::move(Union)); - } - llvm::report_fatal_error( - "found unexpected edge function in 'IIAAAddLabelsEF'"); + static EdgeFunction join(EdgeFunctionRef /*This*/, + const EdgeFunction & /*OtherFunction*/) { + llvm::report_fatal_error("Implemented in 'combine'"); } bool operator==(const IIAAAddLabelsEF &Other) const noexcept { @@ -1112,6 +1031,64 @@ class IDEInstInteractionAnalysisT } }; + const auto &getData(const EdgeFunction &EF) { + if (const auto *AddLabels = llvm::dyn_cast(EF)) { + return AddLabels->Data; + } + if (const auto *KillOrReplace = llvm::dyn_cast(EF)) { + return KillOrReplace->Replacement; + } + llvm::report_fatal_error( + "found unexpected first edge function in 'getData': " + + llvm::Twine(to_string(EF))); + } + + EdgeFunction extend(const EdgeFunction &FirstFunction, + const EdgeFunction &SecondFunction) override { + if (auto Default = defaultComposeOrNull(FirstFunction, SecondFunction)) { + return Default; + } + + const auto &ThisData = getData(FirstFunction); + + if (auto *AD = llvm::dyn_cast(SecondFunction)) { + auto Union = IDEInstInteractionAnalysisT::joinImpl(ThisData, AD->Data); + return llvm::isa(FirstFunction) + ? IIAAAddLabelsEFCache.createEdgeFunction(std::move(Union)) + : IIAAKillOrReplaceEFCache.createEdgeFunction( + std::move(Union)); + } + + llvm::report_fatal_error( + "found unexpected second edge function in 'extend'"); + } + + EdgeFunction combine(const EdgeFunction &FirstFunction, + const EdgeFunction &OtherFunction) override { + /// XXX: Here, we underapproximate joins with EdgeIdentity + if (llvm::isa>(FirstFunction)) { + return OtherFunction; + } + if (llvm::isa>(OtherFunction) && + !llvm::isa>(FirstFunction)) { + return FirstFunction; + } + + if (auto Default = defaultJoinOrNull(FirstFunction, OtherFunction)) { + return Default; + } + + const auto &ThisData = getData(FirstFunction); + const auto &OtherData = getData(OtherFunction); + auto Union = IDEInstInteractionAnalysisT::joinImpl(ThisData, OtherData); + + if (llvm::isa(FirstFunction) && + llvm::isa(OtherFunction)) { + return IIAAKillOrReplaceEFCache.createEdgeFunction(std::move(Union)); + } + return IIAAAddLabelsEFCache.createEdgeFunction(std::move(Union)); + } + // Provide functionalities for printing things and emitting text reports. static void stripBottomResults(std::unordered_map &Res) { @@ -1124,7 +1101,7 @@ class IDEInstInteractionAnalysisT } } - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS = llvm::outs()) override { OS << "\n====================== IDE-Inst-Interaction-Analysis Report " "======================\n"; @@ -1160,7 +1137,7 @@ class IDEInstInteractionAnalysisT /// Computes all variables where a result set has been computed using the /// edge functions (and respective value domain). inline std::unordered_set - getAllVariables(const SolverResults & /* Solution */) const { + getAllVariables(GenericSolverResults /* Solution */) const { std::unordered_set Variables; // collect all variables that are available const llvm::Module *M = this->IRDB->getModule(); @@ -1173,7 +1150,7 @@ class IDEInstInteractionAnalysisT } if (const auto *H = llvm::dyn_cast(I)) { if (!H->isIndirectCall() && H->getCalledFunction() && - this->ICF->isHeapAllocatingFunction(H->getCalledFunction())) { + psr::isHeapAllocatingFunction(H->getCalledFunction())) { Variables.insert(H); } } @@ -1185,7 +1162,7 @@ class IDEInstInteractionAnalysisT /// Computes all variables for which an empty set has been computed using the /// edge functions (and respective value domain). inline std::unordered_set getAllVariablesWithEmptySetValue( - const SolverResults &Solution) const { + GenericSolverResults Solution) const { return removeVariablesWithoutEmptySetValue(Solution, getAllVariables(Solution)); } @@ -1203,18 +1180,8 @@ class IDEInstInteractionAnalysisT } else { auto LSet = std::get>(EdgeFact); OS << "(set size: " << LSet.size() << ") values: "; - if constexpr (std::is_same_v) { - for (const auto &LElem : LSet) { - std::string IRBuffer; - llvm::raw_string_ostream RSO(IRBuffer); - LElem->print(RSO); - RSO.flush(); - OS << IRBuffer << ", "; - } - } else { - for (const auto &LElem : LSet) { - OS << LElem << ", "; - } + for (const auto &LElem : LSet) { + OS << LToString(LElem) << ", "; } } } @@ -1235,33 +1202,29 @@ class IDEInstInteractionAnalysisT /// Filters out all variables that had a non-empty set during edge functions /// computations. inline std::unordered_set removeVariablesWithoutEmptySetValue( - const SolverResults &Solution, + GenericSolverResults Solution, std::unordered_set Variables) const { // Check the solver results and remove all variables for which a // non-empty set has been computed - auto Results = Solution.getAllResultEntries(); - for (const auto &Result : Results) { + // auto Results = Solution.getAllResultEntries(); + Solution.foreachResultEntry([&Variables](const auto &Result) { // We do not care for the concrete instruction at which data-flow facts - // hold, instead we just wish to find out if a variable has been generated - // 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 &Value = Result.getValue(); + // hold, instead we just wish to find out if a variable has been + // generated 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 d_t &Variable = std::get<1>(Result); + const l_t &Value = std::get<2>(Result); // skip result entry if variable is not in the set of all variables - if (Variables.find(Variable) == Variables.end()) { - continue; + if (!Variables.count(Variable)) { + return; } - // skip result entry if the computed value is not of type BitVectorSet - if (!std::holds_alternative>(Value)) { - continue; - } - // remove variable from result set if a non-empty that has been computed - auto &Values = std::get>(Value); - if (!Values.empty()) { + if (const auto *Values = Value.getValueOrNull(); + Values && !Values->empty()) { Variables.erase(Variable); } - } + }); + return Variables; } @@ -1280,4 +1243,36 @@ using IDEInstInteractionAnalysis = IDEInstInteractionAnalysisT<>; } // namespace psr +// Compatibility with llvm::DenseMap/DenseSet: +namespace llvm { +template <> struct DenseMapInfo { + static psr::IDEIIAFlowFact getEmptyKey() { + return psr::IDEIIAFlowFact( + DenseMapInfo::getEmptyKey()); + } + static psr::IDEIIAFlowFact getTombstoneKey() { + return psr::IDEIIAFlowFact( + DenseMapInfo::getTombstoneKey()); + } + static bool isEqual(const psr::IDEIIAFlowFact &L, + const psr::IDEIIAFlowFact &R) { + const auto *Empty = DenseMapInfo::getEmptyKey(); + const auto *TS = DenseMapInfo::getTombstoneKey(); + if (L.getBase() == Empty) { + return R.getBase() == Empty; + } + if (L.getBase() == TS) { + return R.getBase() == TS; + } + if (R.getBase() == Empty || R.getBase() == TS) { + return false; + } + return L == R; + } + static unsigned getHashValue(const psr::IDEIIAFlowFact &FF) { + return std::hash{}(FF); + } +}; +} // namespace llvm + #endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h index 2e7c6d8737..725c60fcc2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h @@ -119,9 +119,10 @@ class IDELinearConstantAnalysis // Helper functions - [[nodiscard]] lca_results_t getLCAResults(SolverResults SR); + [[nodiscard]] lca_results_t + getLCAResults(GenericSolverResults SR); - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS = llvm::outs()) override; private: diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h index 81036cdbe0..1780d2d790 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDESecureHeapPropagation.h @@ -105,7 +105,7 @@ class IDESecureHeapPropagation n_t RetSite, d_t RetSiteNode) override; - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS) override; }; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index be868e5605..35226bf2f7 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -15,25 +15,19 @@ #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 "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 @@ -517,7 +511,7 @@ class IDETypeStateAnalysis return Name.contains(TSD->getTypeNameOfInterest()); } - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS = llvm::outs()) override { LLVMBasedCFG CFG; for (const auto &F : this->IRDB->getAllFunctions()) { @@ -555,6 +549,19 @@ class IDETypeStateAnalysis this->Printer->onFinalize(); } + [[nodiscard]] bool + isInteresting(const llvm::Instruction *Inst) const noexcept { + const auto *Call = llvm::dyn_cast(Inst); + if (!Call) { + return false; + } + if (const auto *StaticCallee = Call->getCalledFunction()) { + return TSD->isAPIFunction(StaticCallee->getName().str()); + } + + return true; + } + private: const TypeStateDescriptionTy *TSD{}; }; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h index 590513a444..8d67741547 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSConstAnalysis.h @@ -144,7 +144,7 @@ class IFDSConstAnalysis [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults 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 deleted file mode 100644 index 135a518113..0000000000 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSFieldSensTaintAnalysis.h +++ /dev/null @@ -1,98 +0,0 @@ -/** - * @author Sebastian Roland - */ - -#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" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/IFDSFieldSensTaintAnalysis/Stats/TraceStats.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" -#include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" -#include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" -#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" - -#include "llvm/IR/Function.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Value.h" - -#include -#include -#include -#include - -namespace llvm { -class Value; -class Function; -class StructType; -} // namespace llvm - -namespace psr { - -struct IFDSFieldSensTaintAnalysisDomain : public LLVMIFDSAnalysisDomainDefault { - using d_t = ExtendedValue; -}; - -class IFDSFieldSensTaintAnalysis - : public IFDSTabulationProblem { -public: - using ConfigurationTy = LLVMTaintConfig; - - IFDSFieldSensTaintAnalysis(const LLVMProjectIRDB *IRDB, - const LLVMTaintConfig *TaintConfig, - std::vector EntryPoints = {"main"}); - - ~IFDSFieldSensTaintAnalysis() override = default; - - FlowFunctionPtrType - getNormalFlowFunction(const llvm::Instruction *Curr, - const llvm::Instruction *Succ) override; - - FlowFunctionPtrType - getCallFlowFunction(const llvm::Instruction *CallSite, - const llvm::Function *DestFun) override; - - FlowFunctionPtrType - getRetFlowFunction(const llvm::Instruction *CallSite, - const llvm::Function *CalleeFun, - const llvm::Instruction *ExitStmt, - const llvm::Instruction *RetSite) override; - - FlowFunctionPtrType - getCallToRetFlowFunction(const llvm::Instruction *CallSite, - const llvm::Instruction *RetSite, - llvm::ArrayRef Callees) override; - - FlowFunctionPtrType - getSummaryFlowFunction(const llvm::Instruction *CallSite, - const llvm::Function *DestFun) override; - - InitialSeeds - initialSeeds() override; - - void - emitTextReport(const SolverResults &SolverResults, - llvm::raw_ostream &OS = llvm::outs()) override; - - [[nodiscard]] ExtendedValue createZeroValue() const { - // create a special value to represent the zero value! - return ExtendedValue(LLVMZeroValue::getInstance()); - } - - [[nodiscard]] bool isZeroValue(ExtendedValue EV) const noexcept override { - return LLVMZeroValue::isLLVMZeroValue(EV.getValue()); - } - -private: - const LLVMTaintConfig *Config{}; - - TraceStats Stats{}; -}; - -std::string DToString(const ExtendedValue &EV); - -} // namespace psr - -#endif diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h index 6c58a032da..5c5f72d24a 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTaintAnalysis.h @@ -11,11 +11,11 @@ #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IFDSTAINTANALYSIS_H #include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFunctionDataFlowFacts.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include -#include #include #include @@ -80,13 +80,17 @@ class IFDSTaintAnalysis bool isZeroValue(d_t FlowFact) const noexcept override; - void emitTextReport(const SolverResults &SR, + void emitTextReport(GenericSolverResults SR, llvm::raw_ostream &OS = llvm::outs()) override; + [[nodiscard]] bool + isInteresting(const llvm::Instruction *Inst) const noexcept; + private: const LLVMTaintConfig *Config{}; LLVMAliasInfoRef PT{}; bool TaintMainArgs{}; + library_summary::LLVMFunctionDataFlowFacts Llvmfdff; bool isSourceCall(const llvm::CallBase *CB, const llvm::Function *Callee) const; @@ -95,9 +99,9 @@ class IFDSTaintAnalysis const llvm::Function *Callee) const; void populateWithMayAliases(container_type &Facts, - const llvm::Instruction *Context) const; + const llvm::Instruction *AliasQueryInst) const; void populateWithMustAliases(container_type &Facts, - const llvm::Instruction *Context) const; + const llvm::Instruction *AliasQueryInst) const; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h index c6a92f457a..5b91aae39e 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.h @@ -62,7 +62,7 @@ class IFDSUninitializedVariables [[nodiscard]] bool isZeroValue(d_t Fact) const noexcept override; - void emitTextReport(const SolverResults &Results, + void emitTextReport(GenericSolverResults Results, llvm::raw_ostream &OS = llvm::outs()) override; [[nodiscard]] const std::map> &getAllUndefUses() const; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h index c7cad79756..9c4da46d74 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h @@ -13,6 +13,8 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" +#include "llvm/Support/raw_ostream.h" + #include #include #include @@ -28,6 +30,12 @@ enum class CSTDFILEIOState { BOT = 4 }; llvm::StringRef to_string(CSTDFILEIOState State) noexcept; + +inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + CSTDFILEIOState State) { + return OS << to_string(State); +} + template <> struct JoinLatticeTraits { static constexpr CSTDFILEIOState top() noexcept { return CSTDFILEIOState::TOP; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h index df91bce7d7..d1e1ffce70 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoFullConstantPropagation.h @@ -32,7 +32,7 @@ class StructType; namespace psr { class LLVMBasedICFG; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class InterMonoFullConstantPropagation : public IntraMonoFullConstantPropagation, @@ -48,7 +48,7 @@ class InterMonoFullConstantPropagation using mono_container_t = IntraMonoFullConstantPropagation::mono_container_t; InterMonoFullConstantPropagation(const LLVMProjectIRDB *IRDB, - const LLVMTypeHierarchy *TH, + const DIBasedTypeHierarchy *TH, const LLVMBasedICFG *ICF, LLVMAliasInfoRef PT, std::vector EntryPoints = {}); diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h index 8674ff564d..a280365a53 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoSolverTest.h @@ -36,7 +36,7 @@ class StructType; namespace psr { -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; struct InterMonoSolverTestDomain : LLVMAnalysisDomainDefault { using mono_container_t = BitVectorSet; @@ -52,8 +52,9 @@ class InterMonoSolverTest : public InterMonoProblem { using i_t = InterMonoSolverTestDomain::i_t; using mono_container_t = InterMonoSolverTestDomain::mono_container_t; - InterMonoSolverTest(const LLVMProjectIRDB *IRDB, const LLVMTypeHierarchy *TH, - const LLVMBasedICFG *ICF, LLVMAliasInfoRef PT, + InterMonoSolverTest(const LLVMProjectIRDB *IRDB, + const DIBasedTypeHierarchy *TH, const LLVMBasedICFG *ICF, + LLVMAliasInfoRef PT, std::vector EntryPoints = {}); ~InterMonoSolverTest() override = default; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h index 211de3a916..7a78746ecf 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/InterMonoTaintAnalysis.h @@ -37,7 +37,7 @@ class StructType; namespace psr { -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; struct InterMonoTaintAnalysisDomain : LLVMAnalysisDomainDefault { using mono_container_t = BitVectorSet; @@ -56,8 +56,9 @@ class InterMonoTaintAnalysis using ConfigurationTy = LLVMTaintConfig; InterMonoTaintAnalysis(const LLVMProjectIRDB *IRDB, - const LLVMTypeHierarchy *TH, const LLVMBasedICFG *ICF, - LLVMAliasInfoRef PT, const LLVMTaintConfig &Config, + const DIBasedTypeHierarchy *TH, + const LLVMBasedICFG *ICF, LLVMAliasInfoRef PT, + const LLVMTaintConfig &Config, std::vector EntryPoints = {}); ~InterMonoTaintAnalysis() override = default; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h index 010e2f517d..0bebfc4e68 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoFullConstantPropagation.h @@ -42,7 +42,7 @@ namespace psr { class LLVMBasedCFG; class LLVMBasedICFG; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class InterMonoFullConstantPropagation; struct IntraMonoFCAFact { @@ -81,7 +81,7 @@ class IntraMonoFullConstantPropagation public: IntraMonoFullConstantPropagation(const LLVMProjectIRDB *IRDB, - const LLVMTypeHierarchy *TH, + const DIBasedTypeHierarchy *TH, const LLVMBasedCFG *CF, LLVMAliasInfoRef PT, std::vector EntryPoints = {}); diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h index 13c5ac42f3..75e31f25ef 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoSolverTest.h @@ -37,7 +37,7 @@ namespace psr { class LLVMBasedCFG; class LLVMBasedICFG; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; struct IntraMonoSolverTestAnalysisDomain : public LLVMAnalysisDomainDefault { using mono_container_t = BitVectorSet; @@ -54,8 +54,9 @@ class IntraMonoSolverTest using i_t = IntraMonoSolverTestAnalysisDomain::i_t; using mono_container_t = IntraMonoSolverTestAnalysisDomain::mono_container_t; - IntraMonoSolverTest(const LLVMProjectIRDB *IRDB, const LLVMTypeHierarchy *TH, - const LLVMBasedCFG *CF, LLVMAliasInfoRef PT, + IntraMonoSolverTest(const LLVMProjectIRDB *IRDB, + const DIBasedTypeHierarchy *TH, const LLVMBasedCFG *CF, + LLVMAliasInfoRef PT, std::vector EntryPoints = {}); ~IntraMonoSolverTest() override = default; diff --git a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h index e813e38d33..b08b91179c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h +++ b/include/phasar/PhasarLLVM/DataFlow/Mono/Problems/IntraMonoUninitVariables.h @@ -28,7 +28,7 @@ class StructType; namespace psr { -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class LLVMBasedCFG; class LLVMBasedICFG; @@ -48,8 +48,8 @@ class IntraMonoUninitVariables using mono_container_t = IntraMonoUninitVariablesDomain::mono_container_t; IntraMonoUninitVariables(const LLVMProjectIRDB *IRDB, - const LLVMTypeHierarchy *TH, const LLVMBasedCFG *CF, - LLVMAliasInfoRef PT, + const DIBasedTypeHierarchy *TH, + const LLVMBasedCFG *CF, LLVMAliasInfoRef PT, std::vector EntryPoints = {}); ~IntraMonoUninitVariables() override = default; diff --git a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h index 11490fe208..755bbc9230 100644 --- a/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h +++ b/include/phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h @@ -17,6 +17,7 @@ class Value; class Instruction; class StructType; class Function; +class DIType; } // namespace llvm namespace psr { @@ -28,7 +29,7 @@ struct LLVMAnalysisDomainDefault : public AnalysisDomain { using d_t = const llvm::Value *; using n_t = const llvm::Instruction *; using f_t = const llvm::Function *; - using t_t = const llvm::StructType *; + using t_t = const llvm::DIType *; using v_t = const llvm::Value *; using c_t = LLVMBasedCFG; using i_t = LLVMBasedICFG; diff --git a/include/phasar/PhasarLLVM/HelperAnalyses.h b/include/phasar/PhasarLLVM/HelperAnalyses.h index f8cb8029b9..2c6f06000b 100644 --- a/include/phasar/PhasarLLVM/HelperAnalyses.h +++ b/include/phasar/PhasarLLVM/HelperAnalyses.h @@ -26,7 +26,7 @@ class Module; namespace psr { class LLVMProjectIRDB; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; class LLVMBasedICFG; class LLVMBasedCFG; class LLVMAliasSet; @@ -60,14 +60,14 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) [[nodiscard]] LLVMProjectIRDB &getProjectIRDB(); [[nodiscard]] LLVMAliasSet &getAliasInfo(); - [[nodiscard]] LLVMTypeHierarchy &getTypeHierarchy(); + [[nodiscard]] DIBasedTypeHierarchy &getTypeHierarchy(); [[nodiscard]] LLVMBasedICFG &getICFG(); [[nodiscard]] LLVMBasedCFG &getCFG(); private: std::unique_ptr IRDB; std::unique_ptr PT; - std::unique_ptr TH; + std::unique_ptr TH; std::unique_ptr ICF; std::unique_ptr CFG; diff --git a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h index a33b6e10d8..4f0e943b78 100644 --- a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h +++ b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h @@ -176,7 +176,9 @@ struct GeneralStatistics { "instead")]] const std::set & getRetResInstructions() const; - [[nodiscard]] nlohmann::json getAsJson() const; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; }; diff --git a/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h new file mode 100644 index 0000000000..91599637c9 --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/FilteredLLVMAliasSet.h @@ -0,0 +1,154 @@ +/****************************************************************************** + * Copyright (c) 2020 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_POINTER_FILTEREDLLVMALIASSET_H +#define PHASAR_PHASARLLVM_POINTER_FILTEREDLLVMALIASSET_H + +#include "phasar/Pointer/AliasAnalysisType.h" +#include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Pointer/AliasResult.h" +#include "phasar/Pointer/AliasSetOwner.h" +#include "phasar/Utils/AnalysisProperties.h" +#include "phasar/Utils/MaybeUniquePtr.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/IR/Function.h" +#include "llvm/Support/ErrorHandling.h" + +#include "nlohmann/json_fwd.hpp" + +#include +#include + +namespace llvm { +class Value; +class Instruction; +} // namespace llvm + +namespace psr { + +class LLVMAliasSet; +class FilteredLLVMAliasSet; + +template <> +struct AliasInfoTraits + : DefaultAATraits {}; + +class FilteredLLVMAliasSet { +public: + using alias_traits_t = AliasInfoTraits; + using n_t = alias_traits_t::n_t; + using v_t = alias_traits_t::v_t; + using AliasSetTy = alias_traits_t::AliasSetTy; + using AliasSetPtrTy = alias_traits_t::AliasSetPtrTy; + using AllocationSiteSetPtrTy = alias_traits_t::AllocationSiteSetPtrTy; + + FilteredLLVMAliasSet(LLVMAliasSet *AS) noexcept; + + FilteredLLVMAliasSet(const FilteredLLVMAliasSet &) = delete; + FilteredLLVMAliasSet &operator=(const FilteredLLVMAliasSet &) = delete; + FilteredLLVMAliasSet &operator=(FilteredLLVMAliasSet &&) noexcept = delete; + + FilteredLLVMAliasSet(FilteredLLVMAliasSet &&) noexcept = default; + + ~FilteredLLVMAliasSet(); + + template >> + explicit FilteredLLVMAliasSet(ArgsT &&...Args) + : FilteredLLVMAliasSet(std::forward(Args)...) {} + + // --- API Functions: + + [[nodiscard]] inline bool isInterProcedural() const noexcept { + return false; + }; + + [[nodiscard]] AliasAnalysisType getAliasAnalysisType() const noexcept; + + [[nodiscard]] AliasResult alias(const llvm::Value *V1, const llvm::Value *V2, + const llvm::Instruction *I); + [[nodiscard]] AliasResult alias(const llvm::Value *V1, const llvm::Value *V2, + const llvm::Function *Fun); + + [[nodiscard]] AliasSetPtrTy getAliasSet(const llvm::Value *V, + const llvm::Instruction *I); + [[nodiscard]] AliasSetPtrTy getAliasSet(const llvm::Value *V, + const llvm::Function *Fun); + + [[nodiscard]] AllocationSiteSetPtrTy + getReachableAllocationSites(const llvm::Value *V, bool IntraProcOnly = false, + const llvm::Instruction *I = nullptr); + + // Checks if PotentialValue is in the reachable allocation sites of V. + [[nodiscard]] bool isInReachableAllocationSites( + const llvm::Value *V, const llvm::Value *PotentialValue, + bool IntraProcOnly = false, const llvm::Instruction *I = nullptr); + + void mergeWith(const FilteredLLVMAliasSet & /*OtherPTI*/) { + llvm::report_fatal_error("Not Supported"); + } + + void introduceAlias(const llvm::Value * /*V1*/, const llvm::Value * /*V2*/, + const llvm::Instruction * /*I*/ = nullptr, + AliasResult /*Kind*/ = AliasResult::MustAlias) { + llvm::report_fatal_error("Not Supported"); + } + + void print(llvm::raw_ostream &OS = llvm::outs()) const; + + [[nodiscard]] nlohmann::json getAsJson() const; + + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; + + [[nodiscard]] AnalysisProperties getAnalysisProperties() const noexcept { + return AnalysisProperties::None; + } + +private: + struct ReachableAllocationSitesKey { + llvm::PointerIntPair FunAndIntraProcOnly; + v_t Value{}; + }; + + struct ReachableAllocationSitesKeyDMI { + inline static ReachableAllocationSitesKey getEmptyKey() noexcept { + return {{}, llvm::DenseMapInfo::getEmptyKey()}; + } + inline static ReachableAllocationSitesKey getTombstoneKey() noexcept { + return {{}, llvm::DenseMapInfo::getTombstoneKey()}; + } + inline static auto getHashValue(ReachableAllocationSitesKey Key) noexcept { + return llvm::hash_combine(Key.FunAndIntraProcOnly.getOpaqueValue(), + Key.Value); + } + inline static bool isEqual(ReachableAllocationSitesKey Key1, + ReachableAllocationSitesKey Key2) noexcept { + return Key1.FunAndIntraProcOnly == Key2.FunAndIntraProcOnly && + Key1.Value == Key2.Value; + } + }; + + FilteredLLVMAliasSet(MaybeUniquePtr AS) noexcept; + + MaybeUniquePtr AS; + AliasSetOwner Owner; + llvm::DenseMap, AliasSetPtrTy> + AliasSetMap; + llvm::DenseMap, + ReachableAllocationSitesKeyDMI> + ReachableAllocationSitesMap; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_POINTER_FILTEREDLLVMALIASSET_H diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h index c266980b16..83c22dbef4 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h @@ -58,8 +58,9 @@ struct AliasInfoTraits * * @brief Represents the points-to graph of a function. */ -class LLVMAliasGraph : public AnalysisPropertiesMixin, - AliasInfoBaseUtils { +class [[deprecated("Use LLVMAliasSet Instead")]] LLVMAliasGraph + : public AnalysisPropertiesMixin, + AliasInfoBaseUtils { using traits_t = AliasInfoTraits; public: @@ -126,9 +127,9 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, * considered. False, if May and Must Aliases should be * considered. */ - explicit LLVMAliasGraph( - LLVMProjectIRDB &IRDB, bool UseLazyEvaluation = true, - AliasAnalysisType PATy = AliasAnalysisType::CFLAnders); + explicit LLVMAliasGraph(LLVMProjectIRDB & IRDB, bool UseLazyEvaluation = true, + AliasAnalysisType PATy = + AliasAnalysisType::CFLAnders); /** * @brief Returns true if graph contains 0 nodes. @@ -162,15 +163,15 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, * @param F Function pointer * @return Vector with pointers. */ - std::vector - getPointersEscapingThroughReturnsForFunction(const llvm::Function *Fd) const; + std::vector getPointersEscapingThroughReturnsForFunction( + const llvm::Function *Fd) const; /** * @brief Checks if a given value is represented by a vertex in the points-to * graph. * @return True, the points-to graph contains the given value. */ - bool containsValue(llvm::Value *V); + bool containsValue(llvm::Value * V); /** * The value-vertex-map maps each Value of the points-to graph to @@ -192,8 +193,8 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, const graph_t &PAG; }; - static inline PointerVertexOrEdgePrinter - makePointerVertexOrEdgePrinter(const graph_t &PAG) { + static inline PointerVertexOrEdgePrinter makePointerVertexOrEdgePrinter( + const graph_t &PAG) { return {PAG}; } @@ -220,9 +221,9 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, AliasSetPtrTy getAliasSet(const llvm::Value *V, const llvm::Instruction *I = nullptr); - AllocationSiteSetPtrTy - getReachableAllocationSites(const llvm::Value *V, bool IntraProcOnly = false, - const llvm::Instruction *I = nullptr); + AllocationSiteSetPtrTy getReachableAllocationSites( + const llvm::Value *V, bool IntraProcOnly = false, + const llvm::Instruction *I = nullptr); [[nodiscard]] bool isInReachableAllocationSites( const llvm::Value *V, const llvm::Value *PotentialValue, @@ -251,7 +252,7 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, void computeAliasGraph(const llvm::Value *V); - void computeAliasGraph(llvm::Function *F); + void computeAliasGraph(llvm::Function * F); struct AllocationSiteDFSVisitor; struct ReachabilityDFSVisitor; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index 0775e98d2a..7a9c2dbd71 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H #define PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" @@ -29,6 +30,8 @@ namespace llvm { class Value; class Instruction; class GlobalVariable; +class GlobalObject; +class DataLayout; class Function; } // namespace llvm @@ -43,6 +46,8 @@ struct AliasInfoTraits class LLVMAliasSet : public AnalysisPropertiesMixin, public AliasInfoBaseUtils { + // For int*IsReachableAllocationSiteTy: + friend class FilteredLLVMAliasSet; public: using traits_t = AliasInfoTraits; @@ -95,7 +100,11 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, void print(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] nlohmann::json getAsJson() const; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const; + + [[nodiscard]] LLVMAliasSetData getLLVMAliasSetData() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h new file mode 100644 index 0000000000..bf924e39cf --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2024 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_TYPEHIERARCHY_LLVMALIASSETDATA_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMALIASSETDATA_H + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMAliasSetData { + std::vector> AliasSets; + std::vector AnalyzedFunctions; + + LLVMAliasSetData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static LLVMAliasSetData deserializeJson(const llvm::Twine &Path); + static LLVMAliasSetData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMALIASSETDATA_H diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h b/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h index c37cfda21c..1195da6c82 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h @@ -18,6 +18,7 @@ namespace llvm { class Value; class Function; class Instruction; +class AAResults; } // namespace llvm namespace psr { @@ -39,8 +40,6 @@ class LLVMBasedAliasAnalysis { LLVMBasedAliasAnalysis &operator=(const LLVMBasedAliasAnalysis &) = delete; ~LLVMBasedAliasAnalysis(); - void print(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] inline llvm::AAResults *getAAResults(llvm::Function *F) { if (!hasAliasInfo(*F)) { computeAliasInfo(*F); diff --git a/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h b/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h index fce91ea9c2..e6e80094a9 100644 --- a/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h +++ b/include/phasar/PhasarLLVM/SimpleAnalysisConstructor.h @@ -19,7 +19,7 @@ namespace psr { class LLVMProjectIRDB; class LLVMAliasSet; class LLVMBasedICFG; -class LLVMTypeHierarchy; +class DIBasedTypeHierarchy; template ProblemTy createAnalysisProblem(HelperAnalyses &HA, ArgTys &&...Args) { @@ -46,13 +46,13 @@ ProblemTy createAnalysisProblem(HelperAnalyses &HA, ArgTys &&...Args) { std::forward(Args)...); } else if constexpr (std::is_constructible_v< ProblemTy, const LLVMProjectIRDB *, - const LLVMTypeHierarchy *, const LLVMBasedCFG *, + const DIBasedTypeHierarchy *, const LLVMBasedCFG *, LLVMAliasSet *, ArgTys...>) { return ProblemTy(&HA.getProjectIRDB(), &HA.getTypeHierarchy(), &HA.getCFG(), &HA.getAliasInfo(), std::forward(Args)...); } else if constexpr (std::is_constructible_v< ProblemTy, const LLVMProjectIRDB *, - const LLVMTypeHierarchy *, const LLVMBasedICFG *, + const DIBasedTypeHierarchy *, const LLVMBasedICFG *, LLVMAliasSet *, ArgTys...>) { return ProblemTy(&HA.getProjectIRDB(), &HA.getTypeHierarchy(), &HA.getICFG(), &HA.getAliasInfo(), diff --git a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h index ba92829e59..c9ddc240cd 100644 --- a/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h +++ b/include/phasar/PhasarLLVM/TaintConfig/TaintConfigData.h @@ -18,18 +18,22 @@ namespace psr { enum class TaintCategory; struct FunctionData { +#if __cplusplus < 202002L FunctionData() noexcept = default; +#endif std::string Name; TaintCategory ReturnCat{}; - std::vector SourceValues; - std::vector SinkValues; - std::vector SanitizerValues; + std::vector SourceValues{}; + std::vector SinkValues{}; + std::vector SanitizerValues{}; bool HasAllSinkParam = false; }; struct VariableData { +#if __cplusplus < 202002L VariableData() noexcept = default; +#endif size_t Line{}; std::string Name; diff --git a/include/phasar/PhasarLLVM/TypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy.h index a8a9b4d12b..7f9c9d6078 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_H #define PHASAR_PHASARLLVM_TYPEHIERARCHY_H +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h index 0048e2ecf7..53b93d9f1a 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H #define PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/TypeHierarchy/TypeHierarchy.h" @@ -19,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/Support/Casting.h" #include @@ -31,18 +33,32 @@ class DIBasedTypeHierarchy using ClassType = const llvm::DIType *; using f_t = const llvm::Function *; + static inline constexpr llvm::StringLiteral StructPrefix = "struct."; + static inline constexpr llvm::StringLiteral ClassPrefix = "class."; + static inline constexpr llvm::StringLiteral VTablePrefix = "_ZTV"; + static inline constexpr llvm::StringLiteral VTablePrefixDemang = + "vtable for "; + static inline constexpr llvm::StringLiteral PureVirtualCallName = + "__cxa_pure_virtual"; + explicit DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB); + explicit DIBasedTypeHierarchy(const LLVMProjectIRDB *IRDB, + const DIBasedTypeHierarchyData &SerializedData); ~DIBasedTypeHierarchy() override = default; + static bool isVTable(llvm::StringRef VarName); + static std::string removeVTablePrefix(llvm::StringRef VarName); + [[nodiscard]] bool hasType(ClassType Type) const override { return TypeToVertex.count(Type); } - [[nodiscard]] bool isSubType(ClassType Type, ClassType SubType) override { + [[nodiscard]] bool isSubType(ClassType Type, + ClassType SubType) const override { return llvm::is_contained(subTypesOf(Type), SubType); } - [[nodiscard]] std::set getSubTypes(ClassType Type) override { + [[nodiscard]] std::set getSubTypes(ClassType Type) const override { const auto &Range = subTypesOf(Type); return {Range.begin(), Range.end()}; } @@ -51,39 +67,31 @@ class DIBasedTypeHierarchy [[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 { + getType(llvm::StringRef TypeName) const noexcept override { return NameToType.lookup(TypeName); } - [[nodiscard]] std::set getAllTypes() const override { + [[nodiscard]] std::vector 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; + [[nodiscard]] llvm::StringRef getTypeName(ClassType Type) const override { + if (const auto *CompTy = llvm::dyn_cast(Type)) { + auto Ident = CompTy->getIdentifier(); + return Ident.empty() ? CompTy->getName() : Ident; } - return &VTables[It->second]; + return Type->getName(); } - [[nodiscard]] size_t size() const override { return VertexTypes.size(); } - - [[nodiscard]] bool empty() const override { return VertexTypes.empty(); } + [[nodiscard]] size_t size() const noexcept override { + return VertexTypes.size(); + } + [[nodiscard]] bool empty() const noexcept override { + return VertexTypes.empty(); + } void print(llvm::raw_ostream &OS = llvm::outs()) const override; @@ -93,9 +101,18 @@ class DIBasedTypeHierarchy */ void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; + + /** + * @brief Prints the class hierarchy to an ostream in json format. + * @param an outputstream + */ + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const override; private: + [[nodiscard]] DIBasedTypeHierarchyData getTypeHierarchyData() const; [[nodiscard]] llvm::iterator_range subTypesOf(size_t TypeIdx) const noexcept; diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h new file mode 100644 index 0000000000..52cd855699 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2024 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_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +struct DIBasedTypeHierarchyData { + // DITypes and llvm::Function * are serialized by serializing their names and + // using the DebugInfoFinder to deserialize them + + std::vector VertexTypes; + std::vector> TransitiveDerivedIndex; + std::vector Hierarchy; + std::vector> VTables; + + DIBasedTypeHierarchyData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static DIBasedTypeHierarchyData deserializeJson(const llvm::Twine &Path); + static DIBasedTypeHierarchyData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h index c5b744e125..3678dfa44c 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h @@ -17,10 +17,12 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHY_H_ #define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHY_H_ +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/TypeHierarchy/TypeHierarchy.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" #include "boost/graph/adjacency_list.hpp" #include "boost/graph/graph_traits.hpp" @@ -108,24 +110,8 @@ class LLVMTypeHierarchy // map from clearname to vtable variable std::unordered_map ClearNameTVMap; - static std::string removeStructOrClassPrefix(const llvm::StructType &T); - - static std::string removeStructOrClassPrefix(const std::string &TypeName); - - static std::string removeTypeInfoPrefix(std::string VarName); - - static std::string removeVTablePrefix(std::string VarName); - - static bool isTypeInfo(const std::string &VarName); - - static bool isVTable(const std::string &VarName); - - static bool isStruct(const llvm::StructType &T); - - static bool isStruct(llvm::StringRef TypeName); - std::vector - getSubTypes(const llvm::Module &M, const llvm::StructType &Type); + getSubTypes(const llvm::Module &M, const llvm::StructType &Type) const; std::vector getVirtualFunctions(const llvm::Module &M, const llvm::StructType &Type); @@ -134,12 +120,24 @@ class LLVMTypeHierarchy void buildLLVMTypeHierarchy(const llvm::Module &M); public: + static bool isTypeInfo(llvm::StringRef VarName); + static bool isVTable(llvm::StringRef VarName); + static bool isStruct(const llvm::StructType &T); + static bool isStruct(llvm::StringRef TypeName); + + static std::string removeStructOrClassPrefix(const llvm::StructType &T); + static std::string removeStructOrClassPrefix(llvm::StringRef TypeName); + static std::string removeTypeInfoPrefix(llvm::StringRef VarName); + static std::string removeVTablePrefix(llvm::StringRef VarName); + /** * @brief Creates a LLVMStructTypeHierarchy based on the * given ProjectIRCompiledDB. * @param IRDB ProjectIRCompiledDB object. */ - LLVMTypeHierarchy(LLVMProjectIRDB &IRDB); + LLVMTypeHierarchy(const LLVMProjectIRDB &IRDB); + LLVMTypeHierarchy(const LLVMProjectIRDB &IRDB, + const LLVMTypeHierarchyData &SerializedData); /** * @brief Creates a LLVMStructTypeHierarchy based on the @@ -166,45 +164,36 @@ class LLVMTypeHierarchy [[nodiscard]] inline bool isSubType(const llvm::StructType *Type, - const llvm::StructType *SubType) override { + const llvm::StructType *SubType) const override { auto ReachableTypes = getSubTypes(Type); return ReachableTypes.count(SubType); } std::set - getSubTypes(const llvm::StructType *Type) override; - - [[nodiscard]] inline bool - isSuperType(const llvm::StructType *Type, - const llvm::StructType *SuperType) override { - return isSubType(SuperType, Type); // NOLINT - } - - std::set - getSuperTypes(const llvm::StructType *Type) override; + getSubTypes(const llvm::StructType *Type) const override; [[nodiscard]] const llvm::StructType * - getType(std::string TypeName) const override; + getType(llvm::StringRef TypeName) const override; - [[nodiscard]] std::set getAllTypes() const override; + [[nodiscard]] std::vector + getAllTypes() const override; - [[nodiscard]] std::string + [[nodiscard]] llvm::StringRef getTypeName(const llvm::StructType *Type) const override; - [[nodiscard]] bool hasVFTable(const llvm::StructType *Type) const override; - - [[nodiscard]] const LLVMVFTable * - getVFTable(const llvm::StructType *Type) const override; - - [[nodiscard]] inline size_t size() const override { + [[nodiscard]] size_t size() const noexcept override { return boost::num_vertices(TypeGraph); }; - [[nodiscard]] inline bool empty() const override { return size() == 0; }; + [[nodiscard]] bool empty() const noexcept override { + return boost::num_vertices(TypeGraph) == 0; + }; void print(llvm::raw_ostream &OS = llvm::outs()) const override; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; // void mergeWith(LLVMTypeHierarchy &Other); @@ -223,7 +212,7 @@ class LLVMTypeHierarchy * @brief Prints the class hierarchy to an ostream in json format. * @param an outputstream */ - void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const override; // void printGraphAsDot(llvm::raw_ostream &out); diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h new file mode 100644 index 0000000000..b9757d0b3c --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.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: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMTypeHierarchyData { + std::string PhasarConfigJsonTypeHierarchyID; + // key = vertex, value = edges + llvm::StringMap> TypeGraph; + + LLVMTypeHierarchyData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static LLVMTypeHierarchyData deserializeJson(const llvm::Twine &Path); + static LLVMTypeHierarchyData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h index fd55a006ab..cd2e226920 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLE_H_ #define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLE_H_ +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" #include "phasar/TypeHierarchy/VFTable.h" #include "nlohmann/json.hpp" @@ -22,6 +23,7 @@ class ConstantStruct; } // namespace llvm namespace psr { +class DIBasedTypeHierarchy; /** * @brief Represents a virtual method table. @@ -30,12 +32,15 @@ namespace psr { * virtual method table matters. */ class LLVMVFTable : public VFTable { + private: - friend class LLVMTypeHierarchy; friend class DIBasedTypeHierarchy; std::vector VFT; public: + // NOLINTNEXTLINE + static constexpr char NullFunName[] = "__null__"; + LLVMVFTable() = default; LLVMVFTable(std::vector Fs) : VFT(std::move(Fs)) {} ~LLVMVFTable() override = default; @@ -66,7 +71,13 @@ class LLVMVFTable : public VFTable { void print(llvm::raw_ostream &OS) const override; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; + + [[nodiscard]] LLVMVFTableData getVFTableData() const; + + void printAsJson(llvm::raw_ostream &OS) const override; [[nodiscard]] std::vector::iterator begin() { return VFT.begin(); diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h new file mode 100644 index 0000000000..e3997d9f53 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2024 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_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMVFTableData { + std::vector VFT; + + LLVMVFTableData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS) const; + + static LLVMVFTableData deserializeJson(const llvm::Twine &Path); + static LLVMVFTableData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ diff --git a/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h b/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h index 4ec3570b52..96e2012d06 100644 --- a/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h +++ b/include/phasar/PhasarLLVM/Utils/BasicBlockOrdering.h @@ -21,7 +21,6 @@ namespace llvm { class Function; class BasicBlock; class Instruction; -class DominatorTree; } // namespace llvm namespace psr { diff --git a/include/phasar/PhasarLLVM/Utils/DataFlowAnalysisType.def b/include/phasar/PhasarLLVM/Utils/DataFlowAnalysisType.def index 5d9b001e2c..5c11905fc1 100644 --- a/include/phasar/PhasarLLVM/Utils/DataFlowAnalysisType.def +++ b/include/phasar/PhasarLLVM/Utils/DataFlowAnalysisType.def @@ -14,15 +14,16 @@ DATA_FLOW_ANALYSIS_TYPES(IFDSUninitializedVariables, "ifds-uninit", "Find usages of uninitialized variables.") DATA_FLOW_ANALYSIS_TYPES(IFDSConstAnalysis, "ifds-const", "Find variables that are actually mutated through the program") DATA_FLOW_ANALYSIS_TYPES(IFDSTaintAnalysis, "ifds-taint", "Simple, alias-aware taint-analysis. Use with --analysis-config") +DATA_FLOW_ANALYSIS_TYPES(SparseIFDSTaintAnalysis, "sparse-ifds-taint", "Simple, alias-aware taint-analysis utilizing SparseIFDS. Use with --analysis-config") DATA_FLOW_ANALYSIS_TYPES(IDEExtendedTaintAnalysis, "ide-xtaint", "More advanced alias-aware taint analysis that provides limited field-sensitivity. Use with --analysis-config") DATA_FLOW_ANALYSIS_TYPES(IFDSTypeAnalysis, "ifds-type", "Simple type analysis") DATA_FLOW_ANALYSIS_TYPES(IDECSTDIOTypeStateAnalysis, "ide-stdio-ts", "Find invalid usages of the libc file-io") DATA_FLOW_ANALYSIS_TYPES(IDEOpenSSLTypeStateAnalysis, "ide-openssl-ts", "Find invalid usages of a subset of the OpenSSL EVP library") DATA_FLOW_ANALYSIS_TYPES(IFDSSolverTest, "ifds-solvertest", "Empty analysis. Just to see that the IFDS solver works") -DATA_FLOW_ANALYSIS_TYPES(IFDSFieldSensTaintAnalysis, "ifds-fstaint", "Specialized taint analysis for tracing environment variables.") DATA_FLOW_ANALYSIS_TYPES(IDELinearConstantAnalysis, "ide-lca", "Simple linear constant propagation") DATA_FLOW_ANALYSIS_TYPES(IDESolverTest, "ide-solvertest", "Empty analysis. Just to see that the IDE solver works") DATA_FLOW_ANALYSIS_TYPES(IDEInstInteractionAnalysis, "ide-iia", "Which instruction has influence on which other instructions?") +DATA_FLOW_ANALYSIS_TYPES(IDEFeatureTaintAnalysis, "ide-fiia", "Which instruction has influence on which other instructions?") DATA_FLOW_ANALYSIS_TYPES(IntraMonoFullConstantPropagation, "intra-mono-fca", "Simple constant propagation without the restriction to linear binary operations. Only works inTRA-procedurally") DATA_FLOW_ANALYSIS_TYPES(IntraMonoSolverTest, "intra-mono-solvertest", "Empty analysis. Just to see that the intraprocedural monotone solver works") DATA_FLOW_ANALYSIS_TYPES(InterMonoSolverTest, "inter-mono-solvertest", "Empty analysis. Just to see that the interprocedural monotone solver works") diff --git a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h index 9432e9c921..f9d3d91d72 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMIRToSrc.h @@ -17,6 +17,8 @@ #ifndef PHASAR_PHASARLLVM_UTILS_LLVMIRTOSRC_H #define PHASAR_PHASARLLVM_UTILS_LLVMIRTOSRC_H +#include "llvm/IR/DebugInfoMetadata.h" + #include "nlohmann/json.hpp" #include @@ -31,11 +33,44 @@ class Value; class GlobalVariable; class Module; class DIFile; +class DIType; +class DILocation; } // namespace llvm namespace psr { +struct DebugLocation { + unsigned Line{}; + unsigned Column{}; + const llvm::DIFile *File{}; +}; + +[[nodiscard]] llvm::DILocalVariable *getDILocalVariable(const llvm::Value *V); + +struct SourceCodeInfo { + std::string SourceCodeLine; + std::string SourceCodeFilename; + std::string SourceCodeFunctionName; + unsigned Line = 0; + unsigned Column = 0; + + [[nodiscard]] bool empty() const noexcept; + + [[nodiscard]] bool operator==(const SourceCodeInfo &Other) const noexcept; + [[nodiscard]] inline bool + operator!=(const SourceCodeInfo &Other) const noexcept { + return !(*this == Other); + } + + /// Similar to operator==, but takes different SourceCodeFileName locations + /// into account + [[nodiscard]] bool equivalentWith(const SourceCodeInfo &Other) const; +}; + +[[nodiscard]] llvm::DILocation *getDILocation(const llvm::Value *V); + [[nodiscard]] std::string getVarNameFromIR(const llvm::Value *V); +[[nodiscard]] llvm::DIType *getVarTypeFromIR(const llvm::Value *V); [[nodiscard]] std::string getFunctionNameFromIR(const llvm::Value *V); @@ -55,29 +90,10 @@ getLineAndColFromIR(const llvm::Value *V); [[nodiscard]] std::string getSrcCodeFromIR(const llvm::Value *V, bool Trim = true); +[[nodiscard]] std::string getSrcCodeFromIR(DebugLocation Loc, bool Trim = true); [[nodiscard]] std::string getModuleIDFromIR(const llvm::Value *V); -struct SourceCodeInfo { - std::string SourceCodeLine; - std::string SourceCodeFilename; - std::string SourceCodeFunctionName; - unsigned Line = 0; - unsigned Column = 0; - - [[nodiscard]] bool empty() const noexcept; - - [[nodiscard]] bool operator==(const SourceCodeInfo &Other) const noexcept; - [[nodiscard]] inline bool - operator!=(const SourceCodeInfo &Other) const noexcept { - return !(*this == Other); - } - - /// Similar to operator==, but takes different SourceCodeFileName locations - /// into account - [[nodiscard]] bool equivalentWith(const SourceCodeInfo &Other) const; -}; - /// Used from the JSON library internally to implicitly convert between json and /// SourceCodeInfo void from_json(const nlohmann::json &J, SourceCodeInfo &Info); @@ -87,12 +103,6 @@ void to_json(nlohmann::json &J, const SourceCodeInfo &Info); [[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); diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index 11342497cb..b0d8bccf9a 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -19,8 +19,6 @@ #include "phasar/Utils/Utilities.h" -#include "llvm/ADT/DenseMap.h" - #include #include @@ -40,14 +38,6 @@ class CallInst; namespace psr { class LLVMProjectIRDB; -/** - * @brief Checks if the given LLVM Value is a LLVM Function Pointer. - * @param V LLVM Value. - * @return True, if given LLVM Value is a LLVM Function Pointer. False, - * otherwise. - */ -bool isFunctionPointer(const llvm::Value *V) noexcept; - /** * @brief Checks if the given LLVM Type is a integer like struct. * @param V LLVM Type. @@ -257,7 +247,7 @@ llvm::StringRef getVarAnnotationIntrinsicName(const llvm::CallInst *CallInst); class ModulesToSlotTracker { friend class LLVMProjectIRDB; - friend class LLVMBasedICFG; + friend class GlobalCtorsDtorsModel; friend class LLVMZeroValue; private: diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index ba9fa480f5..056969fe5e 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -14,6 +14,7 @@ #include "phasar/Pointer/AliasResult.h" #include "phasar/Utils/AnalysisProperties.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -21,8 +22,6 @@ #include "llvm/Support/TypeName.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include #include @@ -140,7 +139,8 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { VT->Print(AA, OS); } - [[nodiscard]] nlohmann::json getAsJson() const { + [[nodiscard, deprecated("Use printAsJson() instead")]] nlohmann::json + getAsJson() const { assert(VT != nullptr); return VT->GetAsJson(AA); } @@ -240,8 +240,14 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->print(OS); }, - [](const void *AA) { - return static_cast(AA)->getAsJson(); + [](const void *AA) noexcept { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" + if constexpr (has_getAsJson::value) { + return static_cast(AA)->getAsJson(); + } + return nlohmann::json(); +#pragma GCC diagnostic pop }, [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->printAsJson(OS); diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index b3bb828c1f..0775346d79 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -11,11 +11,11 @@ #define PHASAR_POINTER_ALIASINFOBASE_H #include "phasar/Pointer/AliasInfoTraits.h" +#include "phasar/Utils/Macros.h" -#include "llvm/ADT/DenseSet.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" #include #include @@ -48,7 +48,7 @@ auto testAliasInfo( CAI.isInterProcedural(), CAI.getAliasAnalysisType(), AI.alias(*VT, *VT, *NT), AI.getAliasSet(*VT, *NT), AI.getReachableAllocationSites(*VT, true, *NT), - AI.isInReachableAllocationSites(*VT, *VT, true, *NT), CAI.getAsJson(), + AI.isInReachableAllocationSites(*VT, *VT, true, *NT), CAI.getAnalysisProperties(), CAI.isContextSensitive(), CAI.isFieldSensitive(), CAI.isFlowSensitive())); template @@ -68,14 +68,13 @@ struct IsAliasInfo< std::tuple::AliasSetPtrTy, typename AliasInfoTraits::AllocationSiteSetPtrTy, bool, - nlohmann::json, AnalysisProperties, bool, bool, bool>, + AnalysisProperties, bool, bool, bool>, decltype(testAliasInfo(std::declval(), std::declval()))>>> : std::true_type { }; } // namespace detail -template -static constexpr bool IsAliasInfo = detail::IsAliasInfo::value; +template PSR_CONCEPT IsAliasInfo = detail::IsAliasInfo::value; } // namespace psr diff --git a/include/phasar/Pointer/AliasInfoTraits.h b/include/phasar/Pointer/AliasInfoTraits.h index 4892db4343..716b317444 100644 --- a/include/phasar/Pointer/AliasInfoTraits.h +++ b/include/phasar/Pointer/AliasInfoTraits.h @@ -11,11 +11,10 @@ #define PHASAR_POINTER_ALIASINFOTRAITS_H #include "phasar/Utils/BoxedPointer.h" +#include "phasar/Utils/MaybeUniquePtr.h" #include "llvm/ADT/DenseSet.h" -#include - namespace psr { template struct AliasInfoTraits { @@ -31,7 +30,7 @@ template struct DefaultAATraits { using v_t = V; using AliasSetTy = llvm::DenseSet; using AliasSetPtrTy = BoxedConstPtr; - using AllocationSiteSetPtrTy = std::unique_ptr; + using AllocationSiteSetPtrTy = MaybeUniquePtr; }; } // namespace psr diff --git a/include/phasar/Pointer/PointsToInfo.h b/include/phasar/Pointer/PointsToInfo.h index 94bae1f3d8..2eaf0f4387 100644 --- a/include/phasar/Pointer/PointsToInfo.h +++ b/include/phasar/Pointer/PointsToInfo.h @@ -14,10 +14,10 @@ #include "phasar/Utils/ByRef.h" #include -#include #include #include #include +#include namespace psr { @@ -39,8 +39,10 @@ struct PointsToTraits> : PTATraits {}; template class PointsToInfoRef>> - : PointsToInfoBase> { + : public PointsToInfoBase> { friend class PointsToInfo; + friend PointsToInfoBase>; + using base_t = PointsToInfoBase>; public: diff --git a/include/phasar/Pointer/PointsToInfoBase.h b/include/phasar/Pointer/PointsToInfoBase.h index 6960656465..9eef7cf3ad 100644 --- a/include/phasar/Pointer/PointsToInfoBase.h +++ b/include/phasar/Pointer/PointsToInfoBase.h @@ -36,12 +36,12 @@ struct is_PointsToTraits< : std::true_type {}; template -static constexpr bool is_PointsToTraits_v = // NOLINT +PSR_CONCEPT is_PointsToTraits_v = // NOLINT is_PointsToTraits::value; // clang-format off template -static constexpr bool is_equivalent_PointsToTraits_v = // NOLINT +PSR_CONCEPT is_equivalent_PointsToTraits_v = // NOLINT is_PointsToTraits_v && is_PointsToTraits_v && std::is_same_v && std::is_same_v && diff --git a/include/phasar/TypeHierarchy/TypeHierarchy.h b/include/phasar/TypeHierarchy/TypeHierarchy.h index e3f44e8791..d753652cf9 100644 --- a/include/phasar/TypeHierarchy/TypeHierarchy.h +++ b/include/phasar/TypeHierarchy/TypeHierarchy.h @@ -10,14 +10,13 @@ #ifndef PHASAR_TYPEHIERARCHY_TYPEHIERARCHY_H #define PHASAR_TYPEHIERARCHY_TYPEHIERARCHY_H -#include "phasar/TypeHierarchy/VFTable.h" +#include "phasar/Utils/Nullable.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" +#include "nlohmann/json_fwd.hpp" #include -#include namespace psr { @@ -25,33 +24,27 @@ template class TypeHierarchy { public: virtual ~TypeHierarchy() = default; - virtual bool hasType(T Type) const = 0; + [[nodiscard]] virtual bool hasType(T Type) const = 0; + [[nodiscard]] virtual bool isSubType(T Type, T SubType) const = 0; - virtual bool isSubType(T Type, T SubType) = 0; + [[nodiscard]] virtual std::set getSubTypes(T Type) const = 0; - virtual std::set getSubTypes(T Type) = 0; + [[nodiscard]] virtual Nullable getType(llvm::StringRef TypeName) const = 0; - virtual bool isSuperType(T Type, T SuperType) = 0; + [[nodiscard]] virtual std::vector getAllTypes() const = 0; - virtual std::set getSuperTypes(T Type) = 0; + [[nodiscard]] virtual llvm::StringRef getTypeName(T Type) const = 0; - [[nodiscard]] virtual T getType(std::string TypeName) const = 0; - - [[nodiscard]] virtual std::set getAllTypes() const = 0; - - [[nodiscard]] virtual std::string getTypeName(T Type) const = 0; - - [[nodiscard]] virtual bool hasVFTable(T Type) const = 0; - - [[nodiscard]] virtual const VFTable *getVFTable(T Type) const = 0; - - [[nodiscard]] virtual size_t size() const = 0; - - [[nodiscard]] virtual bool empty() const = 0; + [[nodiscard]] virtual size_t size() const noexcept = 0; + [[nodiscard]] virtual bool empty() const noexcept = 0; virtual void print(llvm::raw_ostream &OS = llvm::outs()) const = 0; - [[nodiscard]] virtual nlohmann::json getAsJson() const = 0; + [[nodiscard, + deprecated("Please use printAsJson() instead")]] virtual nlohmann::json + getAsJson() const = 0; + + virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; template diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index 56334171b0..83c544dbcd 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -38,7 +38,11 @@ template class VFTable { virtual void print(llvm::raw_ostream &OS) const = 0; - [[nodiscard]] virtual nlohmann::json getAsJson() const = 0; + [[nodiscard, + deprecated("Please use printAsJson() instead")]] virtual nlohmann::json + getAsJson() const = 0; + + virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; template diff --git a/include/phasar/Utils/AdjacencyList.h b/include/phasar/Utils/AdjacencyList.h index e09bb99fa5..b69abe74cf 100644 --- a/include/phasar/Utils/AdjacencyList.h +++ b/include/phasar/Utils/AdjacencyList.h @@ -163,7 +163,7 @@ struct GraphTraits> { if constexpr (!std::is_same_v) { assert(G.Adj.size() == G.Nodes.size()); } - return psr::iota(size_t(0), G.Adj.size()); + return psr::iota(vertex_t(0), G.Adj.size()); } /// Gets the node-tag for node Vtx in graph G. Vtx must be part of G @@ -292,8 +292,8 @@ struct GraphTraits> { #if __cplusplus >= 202002L static_assert(is_graph>); - static_assert(is_reservable_graph_trait>>); #endif + static_assert(is_reservable_graph_trait_v>>); }; } // namespace psr diff --git a/include/phasar/Utils/BitVectorSet.h b/include/phasar/Utils/BitVectorSet.h index 418d90e603..c64f8f41bf 100644 --- a/include/phasar/Utils/BitVectorSet.h +++ b/include/phasar/Utils/BitVectorSet.h @@ -12,6 +12,7 @@ #include "llvm/ADT/BitVector.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" @@ -20,16 +21,18 @@ #include #include +#include #include namespace psr { namespace internal { + inline bool isLess(const llvm::BitVector &Lhs, const llvm::BitVector &Rhs) { unsigned LhsBits = Lhs.size(); unsigned RhsBits = Rhs.size(); if (LhsBits > RhsBits) { - if (Lhs.find_first_in(RhsBits, LhsBits) != -1) { + if (Lhs.find_next(RhsBits) != -1) { return false; } } else if (LhsBits < RhsBits) { @@ -47,6 +50,15 @@ inline bool isLess(const llvm::BitVector &Lhs, const llvm::BitVector &Rhs) { } return false; } + +inline llvm::ArrayRef getWords(const llvm::BitVector &BV, + uintptr_t & /*Store*/) { + return BV.getData(); +} +inline llvm::ArrayRef getWords(const llvm::SmallBitVector &BV, + uintptr_t &Store) { + return BV.getData(Store); +} } // namespace internal /** @@ -56,19 +68,22 @@ inline bool isLess(const llvm::BitVector &Lhs, const llvm::BitVector &Rhs) { * * @brief Implements a set that requires minimal space. */ -template class BitVectorSet { -private: +template +class BitVectorSet { +public: // Using boost::hash causes ambiguity for hash_value(): // - // - // - using bimap_t = boost::bimap>, boost::bimaps::unordered_set_of>; + +private: inline static bimap_t Position; // NOLINT - llvm::BitVector Bits; + BitVectorTy Bits; template class BitVectorSetIterator { - llvm::BitVector Bits; + BitVectorTy Bits; public: using iterator_category = std::forward_iterator_tag; @@ -83,7 +98,7 @@ template class BitVectorSet { return *this; } - void setBits(const llvm::BitVector &OtherBits) { Bits = OtherBits; } + void setBits(const BitVectorTy &OtherBits) { Bits = OtherBits; } bool operator==(const BitVectorSetIterator &OtherIterator) const { return PosPtr == OtherIterator.getPtr(); @@ -152,7 +167,7 @@ template class BitVectorSet { // T getVal() {return pos_ptr->second;} - [[nodiscard]] llvm::BitVector getBits() const { return Bits; } + [[nodiscard]] const BitVectorTy &getBits() const { return Bits; } private: D PosPtr; @@ -176,12 +191,18 @@ template class BitVectorSet { insert(First, Last); } + static BitVectorSet fromBits(BitVectorTy Bits) { + BitVectorSet Ret; + Ret.Bits = std::move(Bits); + return Ret; + } + [[nodiscard]] BitVectorSet setUnion(const BitVectorSet &Other) const { - size_t MaxSize = std::max(Bits.size(), Other.Bits.size()); - BitVectorSet Res; - Res.Bits.reserve(MaxSize); - Res.Bits = Bits; - Res.Bits |= Other.Bits; + const bool ThisSetIsSmaller = Bits.size() < Other.Bits.size(); + BitVectorSet Res = ThisSetIsSmaller ? Other : *this; + const BitVectorSet &Smaller = ThisSetIsSmaller ? *this : Other; + + Res.Bits |= Smaller.Bits; return Res; } @@ -271,6 +292,10 @@ template class BitVectorSet { [[nodiscard]] size_t size() const noexcept { return Bits.count(); } + [[nodiscard]] const BitVectorTy &getBits() const &noexcept { return Bits; } + [[nodiscard]] BitVectorTy &getBits() &noexcept { return Bits; } + [[nodiscard]] BitVectorTy &&getBits() &&noexcept { return std::move(Bits); } + friend bool operator==(const BitVectorSet &Lhs, const BitVectorSet &Rhs) { bool LeftEmpty = Lhs.empty(); bool RightEmpty = Rhs.empty(); @@ -279,8 +304,10 @@ template class BitVectorSet { } // Check, whether Lhs and Rhs actually have the same bits set and not // whether their internal representation is exactly identitcal - auto LhsWords = Lhs.Bits.getData(); - auto RhsWords = Rhs.Bits.getData(); + + uintptr_t LStore{}, RStore{}; + auto LhsWords = internal::getWords(Lhs.Bits, LStore); + auto RhsWords = internal::getWords(Rhs.Bits, RStore); if (LhsWords.size() == RhsWords.size()) { return LhsWords == RhsWords; } @@ -307,7 +334,8 @@ template class BitVectorSet { if (BV.Bits.empty()) { return {}; } - auto Words = BV.Bits.getData(); + uintptr_t Store{}; + auto Words = internal::getWords(BV.Bits, Store); size_t Idx = Words.size(); while (Idx && Words[Idx - 1] == 0) { --Idx; @@ -366,6 +394,19 @@ template class BitVectorSet { } }; +// Overloads with the other intersectWith functions from Utilities.h +template +void intersectWith(BitVectorSet &Dest, const BitVectorSet &Src) { + Dest.setIntersectWith(Src); +} } // namespace psr +namespace std { +template struct hash> { + size_t operator()(const psr::BitVectorSet &BVS) noexcept { + return hash_value(BVS); + } +}; +} // namespace std + #endif diff --git a/include/phasar/Utils/ByRef.h b/include/phasar/Utils/ByRef.h index 4e0f21bed7..e36d7a0364 100644 --- a/include/phasar/Utils/ByRef.h +++ b/include/phasar/Utils/ByRef.h @@ -10,12 +10,14 @@ #ifndef PHASAR_UTILS_BYREF_H #define PHASAR_UTILS_BYREF_H +#include "phasar/Utils/Macros.h" + #include namespace psr { template -static constexpr bool CanEfficientlyPassByValue = +PSR_CONCEPT CanEfficientlyPassByValue = sizeof(T) <= 2 * sizeof(void *) && std::is_trivially_copyable_v; template diff --git a/include/phasar/Utils/DebugOutput.h b/include/phasar/Utils/DebugOutput.h index e904ef5310..03f6098b38 100644 --- a/include/phasar/Utils/DebugOutput.h +++ b/include/phasar/Utils/DebugOutput.h @@ -10,9 +10,7 @@ #define PHASAR_UTILS_DEBUGOUTPUT_H #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/TypeTraits.h" -#include "llvm/ADT/SmallBitVector.h" #include "llvm/IR/Type.h" #include "llvm/Support/raw_os_ostream.h" #include "llvm/Support/raw_ostream.h" diff --git a/include/phasar/Utils/EmptyBaseOptimizationUtils.h b/include/phasar/Utils/EmptyBaseOptimizationUtils.h new file mode 100644 index 0000000000..a592780bff --- /dev/null +++ b/include/phasar/Utils/EmptyBaseOptimizationUtils.h @@ -0,0 +1,82 @@ +#ifndef PHASAR_UTILS_EMPTYBASEOPTIMIZATIONUTILS_H +#define PHASAR_UTILS_EMPTYBASEOPTIMIZATIONUTILS_H + +#include "phasar/Utils/ByRef.h" + +#include "llvm/ADT/DenseMapInfo.h" + +#include +#include + +namespace psr { +/// A dummy type that takes no space in memory when used with the empty-base +/// optimization or with [[no_unique_address]] +struct EmptyType { + constexpr friend bool operator==(EmptyType /*LHS*/, + EmptyType /*RHS*/) noexcept { + return true; + } + constexpr friend bool operator!=(EmptyType /*LHS*/, + EmptyType /*RHS*/) noexcept { + return false; + } +}; + +/// A wrapper over a single object that pretends to be a std::pair +template struct DummyPair { + T first; // NOLINT -- Need to have the same interface as std::pair + [[no_unique_address]] EmptyType second; // NOLINT -- '' + + [[nodiscard]] auto getHashCode() const noexcept { + return std::hash{}(first); + } + template + friend std::enable_if_t, bool> + operator==(DummyPair LHS, + DummyPair RHS) noexcept(noexcept(LHS.first == RHS.first)) { + return LHS.first == RHS.first; + } + + template + friend std::enable_if_t, bool> + operator==(const DummyPair &LHS, + const DummyPair &RHS) noexcept(noexcept(LHS.first == RHS.first)) { + return LHS.first == RHS.first; + } + + template + friend std::enable_if_t, bool> + operator!=(DummyPair LHS, DummyPair RHS) noexcept(noexcept(LHS == RHS)) { + return !(LHS == RHS); + } + + template + friend std::enable_if_t, bool> + operator!=(const DummyPair &LHS, + const DummyPair &RHS) noexcept(noexcept(LHS == RHS)) { + return !(LHS == RHS); + } +}; +} // namespace psr + +namespace llvm { +template struct DenseMapInfo> { + using value_type = psr::DummyPair; + + static value_type getEmptyKey() noexcept { + return {DenseMapInfo::getEmptyKey(), {}}; + } + static value_type getTombstoneKey() noexcept { + return {DenseMapInfo::getTombstoneKey(), {}}; + } + static auto getHashValue(psr::ByConstRef DP) noexcept { + return DP.getHashCode(); + } + static bool isEqual(psr::ByConstRef LHS, + psr::ByConstRef RHS) noexcept { + return DenseMapInfo::isEqual(LHS.first, RHS.first); + } +}; +} // namespace llvm + +#endif // PHASAR_UTILS_EMPTYBASEOPTIMIZATIONUTILS_H_ diff --git a/include/phasar/Utils/EquivalenceClassMap.h b/include/phasar/Utils/EquivalenceClassMap.h index c93508fe3c..a11d3fe4ed 100644 --- a/include/phasar/Utils/EquivalenceClassMap.h +++ b/include/phasar/Utils/EquivalenceClassMap.h @@ -10,13 +10,16 @@ #ifndef PHASAR_UTILS_EQUIVALENCECLASSMAP_H #define PHASAR_UTILS_EQUIVALENCECLASSMAP_H +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/iterator_range.h" +#include #include +#include #include #include -#include namespace psr { @@ -28,7 +31,9 @@ namespace psr { template struct EquivalenceClassMap { template using SetType = std::set; using EquivalenceClassBucketT = std::pair, ValueT>; - using StorageT = std::vector; + // Use SmallVector here, since it has a smaller struct-size than std::vector; + // we may store a lot of them in the FlowEdgeFunctionCache + using StorageT = llvm::SmallVector; public: using size_type = size_t; @@ -169,6 +174,126 @@ template struct EquivalenceClassMap { StorageT StoredData{}; }; +template > +class EquivalenceClassMapNG { + using SetTy = llvm::SmallDenseSet; + +public: + // NOLINTNEXTLINE(readability-identifier-naming) + class const_iterator { + + public: + using value_type = std::pair; + using reference = std::pair; + using pointer = reference *; + using difference_type = ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + const_iterator &operator++() noexcept { + ++Val; + ++Ky; + return *this; + } + + reference operator*() noexcept { return reference(*Ky, *Val); } + + pointer operator->() noexcept { + TempStorage.emplace(*Ky, *Val); + return &*TempStorage; + } + + bool operator==(const const_iterator &Other) const noexcept { + return Val == Other.Val; + } + + bool operator!=(const const_iterator &Other) const noexcept { + return !(*this == Other); + } + + private: + friend EquivalenceClassMapNG; + + const_iterator(const TValue *Val, const SetTy *Ky) noexcept + : Val(Val), Ky(Ky) {} + + const TValue *Val; + const SetTy *Ky; + + std::optional TempStorage; + }; + + EquivalenceClassMapNG() noexcept = default; + + void reserve(size_t InitialCapacity) { + Values.reserve(InitialCapacity); + Keys.reserve(InitialCapacity); + } + + template + std::pair insert(KK &&Key, VV &&Value) { + ValueComparator VComp; + for (size_t I = 0, End = Values.size(); I != End; ++I) { + if (VComp(Values[I], Value)) { + return {getIterator(I), Keys[I].insert(std::forward(Key)).second}; + } + } + + Values.emplace_back(std::forward(Value)); + Keys.emplace_back().insert(std::forward(Key)); + return {getIterator(Values.size() - 1), true}; + } + + template + const TValue &getOrInsertLazy(KK &&Key, VCtor &&MakeV) { + for (size_t I = 0, End = Keys.size(); I != End; ++I) { + if (Keys[I].count(Key)) { + return Values[I]; + } + } + return (*insert(std::forward(Key), std::invoke(MakeV)).first).second; + } + + const_iterator begin() const noexcept { + return {Values.begin(), Keys.begin()}; + } + const_iterator end() const noexcept { return {Values.end(), Keys.end()}; } + + const_iterator find(const TKey &Ky) const { + for (size_t I = 0, End = Keys.size(); I < End; ++I) { + if (Keys[I].count(Ky)) { + return getIterator(I); + } + } + + return end(); + } + + [[nodiscard]] inline size_t numEquivalenceClasses() const noexcept { + return Values.size(); + } + + // Returns the size of the map, i.e., the number of equivalence classes. + [[nodiscard]] inline size_t size() const noexcept { + return numEquivalenceClasses(); + } + + [[nodiscard]] bool empty() const noexcept { return Values.empty(); } + + void clear() noexcept { + Values.clear(); + Keys.clear(); + } + +private: + const_iterator getIterator(size_t I) const noexcept { + return {std::next(Values.begin(), I), std::next(Keys.begin(), I)}; + } + + llvm::SmallVector Values; + llvm::SmallVector Keys; +}; + } // namespace psr #endif diff --git a/include/phasar/Utils/GraphTraits.h b/include/phasar/Utils/GraphTraits.h index be00b4b84f..de96b9cc20 100644 --- a/include/phasar/Utils/GraphTraits.h +++ b/include/phasar/Utils/GraphTraits.h @@ -10,17 +10,15 @@ #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" +#if __cplusplus >= 202002L #include +#endif #include #include #include @@ -69,7 +67,7 @@ concept is_graph_trait = requires(typename GraphTrait::graph_type &graph, } -> psr::is_iterable_over_v; { GraphTrait::vertices(cgraph) - } -> psr::is_iterable_over_v; + } -> psr::is_iterable_over_v; { GraphTrait::node(cgraph, vtx) } -> std::convertible_to; @@ -101,6 +99,18 @@ concept is_reservable_graph_trait_v = is_graph_trait && {GraphTrait::reserve(g, size_t(0))}; }; +template +concept is_removable_graph_trait_v = is_graph_trait && + requires(typename GraphTrait::graph_type &g, + typename GraphTrait::vertex_t vtx, + typename GraphTrait::edge_iterator edge_it, + typename GraphTrait::roots_iterator root_it) { + typename GraphTrait::edge_iterator; + typename GraphTrait::roots_iterator; + {GraphTrait::removeEdge(g, vtx, edge_it)}; + {GraphTrait::removeRoot(g, root_it)}; +}; + #else namespace detail { template diff --git a/include/phasar/Utils/InitPhasar.h b/include/phasar/Utils/InitPhasar.h new file mode 100644 index 0000000000..57bb37e5a4 --- /dev/null +++ b/include/phasar/Utils/InitPhasar.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#include "llvm/Support/InitLLVM.h" + +namespace psr { +class InitPhasar : llvm::InitLLVM { +public: + InitPhasar(int &Argc, const char **&Argv) noexcept; + InitPhasar(int &Argc, char **&Argv) noexcept + : InitPhasar(Argc, (const char **&)Argv) {} +}; +} // namespace psr + +#define PSR_INITIALIZER(argc, argv) \ + const ::psr::InitPhasar PsrInitializerVar((argc), (argv)) diff --git a/include/phasar/Utils/IotaIterator.h b/include/phasar/Utils/IotaIterator.h index 9eab786b61..9b55162717 100644 --- a/include/phasar/Utils/IotaIterator.h +++ b/include/phasar/Utils/IotaIterator.h @@ -16,7 +16,6 @@ #include #include -#include #include namespace psr { @@ -59,7 +58,8 @@ template class IotaIterator { template using IotaRangeType = llvm::iterator_range>; -template constexpr auto iota(T From, T To) noexcept { +template +constexpr auto iota(T From, type_identity_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)); diff --git a/include/phasar/Utils/JoinLattice.h b/include/phasar/Utils/JoinLattice.h index 8e748cf780..bfb0f0b02d 100644 --- a/include/phasar/Utils/JoinLattice.h +++ b/include/phasar/Utils/JoinLattice.h @@ -17,6 +17,8 @@ #ifndef PHASAR_UTILS_JOINLATTICE_H #define PHASAR_UTILS_JOINLATTICE_H +#include "phasar/Utils/Macros.h" + #include #include @@ -41,8 +43,7 @@ struct HasJoinLatticeTraitsHelper< L>>> : std::true_type {}; } // namespace detail template -static constexpr bool HasJoinLatticeTraits = - detail::HasJoinLatticeTraitsHelper::value; +PSR_CONCEPT HasJoinLatticeTraits = detail::HasJoinLatticeTraitsHelper::value; template class JoinLattice { public: diff --git a/include/phasar/Controller.h b/include/phasar/Utils/Macros.h similarity index 51% rename from include/phasar/Controller.h rename to include/phasar/Utils/Macros.h index 92d4c22029..9620018de8 100644 --- a/include/phasar/Controller.h +++ b/include/phasar/Utils/Macros.h @@ -1,5 +1,5 @@ /****************************************************************************** - * Copyright (c) 2023 Fabian Schiebel. + * Copyright (c) 2024 Fabian Schiebel. * All rights reserved. This program and the accompanying materials are made * available under the terms of LICENSE.txt. * @@ -7,10 +7,15 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_CONTROLLER_H -#define PHASAR_CONTROLLER_H +#ifndef PHASAR_UTILS_MACROS_H +#define PHASAR_UTILS_MACROS_H -#include "phasar/Controller/AnalysisController.h" -#include "phasar/Controller/AnalysisControllerEmitterOptions.h" +#define PSR_FWD(...) ::std::forward(__VA_ARGS__) -#endif // PHASAR_CONTROLLER_H +#if __cplusplus < 202002L +#define PSR_CONCEPT static constexpr bool +#else +#define PSR_CONCEPT concept +#endif + +#endif // PHASAR_UTILS_MACROS_H diff --git a/include/phasar/Utils/MaybeUniquePtr.h b/include/phasar/Utils/MaybeUniquePtr.h index fe086fc85e..4e50f2304f 100644 --- a/include/phasar/Utils/MaybeUniquePtr.h +++ b/include/phasar/Utils/MaybeUniquePtr.h @@ -59,7 +59,8 @@ template class MaybeUniquePtrBase { /// \tparam RequireAlignment If true, the datastructure only works if /// alignof(T) > 1 holds. Enables incomplete T types template -class MaybeUniquePtr : detail::MaybeUniquePtrBase { +class [[clang::trivial_abi]] MaybeUniquePtr + : detail::MaybeUniquePtrBase { using detail::MaybeUniquePtrBase::Data; public: @@ -79,8 +80,9 @@ class MaybeUniquePtr : detail::MaybeUniquePtrBase { : MaybeUniquePtr(Owner.release(), true) {} constexpr MaybeUniquePtr(MaybeUniquePtr &&Other) noexcept - : detail::MaybeUniquePtrBase( - std::exchange(Other.Data, {})) {} + : detail::MaybeUniquePtrBase(std::move(Other)) { + Other.Data = {}; + } constexpr void swap(MaybeUniquePtr &Other) noexcept { std::swap(Data, Other.Data); diff --git a/include/phasar/Utils/Nullable.h b/include/phasar/Utils/Nullable.h index 5bb3b2a9da..829db85eab 100644 --- a/include/phasar/Utils/Nullable.h +++ b/include/phasar/Utils/Nullable.h @@ -12,12 +12,34 @@ #include #include +#include namespace psr { template using Nullable = std::conditional_t, T, std::optional>; + +template +std::enable_if_t, T &&> +unwrapNullable(T &&Val) noexcept { + return std::forward(Val); +} +template +std::enable_if_t, T> +unwrapNullable(std::optional &&Val) noexcept { + return *std::move(Val); +} +template +std::enable_if_t, const T &> +unwrapNullable(const std::optional &Val) noexcept { + return *Val; +} +template +std::enable_if_t, T &> +unwrapNullable(std::optional &Val) noexcept { + return *Val; +} } // namespace psr #endif // PHASAR_UTILS_NULLABLE_H diff --git a/include/phasar/Utils/PAMM.h b/include/phasar/Utils/PAMM.h index 50f8f215c2..00f4bb3c78 100644 --- a/include/phasar/Utils/PAMM.h +++ b/include/phasar/Utils/PAMM.h @@ -17,8 +17,6 @@ #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" diff --git a/include/phasar/Utils/PointerUtils.h b/include/phasar/Utils/PointerUtils.h new file mode 100644 index 0000000000..117a5bea90 --- /dev/null +++ b/include/phasar/Utils/PointerUtils.h @@ -0,0 +1,29 @@ +#ifndef PHASAR_UTILS_POINTERUTILS_H +#define PHASAR_UTILS_POINTERUTILS_H + +#include "llvm/ADT/IntrusiveRefCntPtr.h" + +#include + +namespace psr { + +/// A simple helper function to get a raw pointer from an arbitrary pointer type +/// in generic code. This overload set is extendable. + +template T *getPointerFrom(T *Ptr) noexcept { return Ptr; } +template +constexpr T *getPointerFrom(const std::unique_ptr &Ptr) noexcept { + return Ptr.get(); +} +template +constexpr T *getPointerFrom(const std::shared_ptr &Ptr) noexcept { + return Ptr.get(); +} +template +constexpr T *getPointerFrom(const llvm::IntrusiveRefCntPtr &Ptr) noexcept { + return Ptr.get(); +} + +} // namespace psr + +#endif // PHASAR_UTILS_POINTERUTILS_H diff --git a/include/phasar/Utils/Printer.h b/include/phasar/Utils/Printer.h index 31a08cdb4a..76cb2e5e55 100644 --- a/include/phasar/Utils/Printer.h +++ b/include/phasar/Utils/Printer.h @@ -27,7 +27,7 @@ class Function; namespace psr { namespace detail { template -static constexpr bool IsSomehowPrintable = +PSR_CONCEPT IsSomehowPrintable = has_str_v || is_llvm_printable_v || has_adl_to_string_v; template decltype(auto) printSomehow(const T &Val) { diff --git a/include/phasar/Utils/SemiRing.h b/include/phasar/Utils/SemiRing.h new file mode 100644 index 0000000000..e4999d329e --- /dev/null +++ b/include/phasar/Utils/SemiRing.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * 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 and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_SEMIRING_H +#define PHASAR_UTILS_SEMIRING_H + +#include "phasar/DataFlow/IfdsIde/EdgeFunction.h" + +namespace psr { +template class SemiRing { +public: + using l_t = typename AnalysisDomainTy::l_t; + + virtual ~SemiRing() = default; + + virtual EdgeFunction extend(const EdgeFunction &L, + const EdgeFunction &R) { + return L.composeWith(R); + } + + virtual EdgeFunction combine(const EdgeFunction &L, + const EdgeFunction &R) { + return L.joinWith(R); + } +}; +} // namespace psr + +#endif // PHASAR_UTILS_SEMIRING_H diff --git a/include/phasar/Utils/StableVector.h b/include/phasar/Utils/StableVector.h index 12c18853ec..8406bea98a 100644 --- a/include/phasar/Utils/StableVector.h +++ b/include/phasar/Utils/StableVector.h @@ -320,12 +320,20 @@ class StableVector { /// In theory, we could allocate as many blocks as necessary, such that the /// accumulated size of them is exactly SIZE_MAX+1, but this will already /// overflow the Size field and we still need to store the blocks and the - /// other metadata somewhere, such that we will never be able to allocate - /// the last block (with size SIZE_MAX/2). So, the maximum number of + /// other metadata somewhere, so we will never be able to allocate + /// the last block (with size SIZE_MAX/2). Hence, the maximum number of /// elements will be SIZE_MAX/2; return (SIZE_MAX / 2) / sizeof(T); }; + [[nodiscard]] size_t capacity() const noexcept { + return llvm::NextPowerOf2(std::max(InitialCapacity, size())); + } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return capacity() * sizeof(T) + Blocks.capacity_in_bytes(); + } + [[nodiscard]] T &front() noexcept { assert(!empty() && "Do not call front() on an empty StableVector!"); return *Blocks[0]; diff --git a/include/phasar/Utils/Table.h b/include/phasar/Utils/Table.h index 24ac50ac05..0a8a3b33ac 100644 --- a/include/phasar/Utils/Table.h +++ b/include/phasar/Utils/Table.h @@ -22,6 +22,7 @@ #include "llvm/Support/raw_ostream.h" +#include #include #include #include @@ -82,6 +83,24 @@ template class Table { [[nodiscard]] size_t size() const noexcept { return Tab.size(); } + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + size_t Sz = + Tab.bucket_count() * sizeof(void *) + + Tab.size() * + sizeof( + std::tuple); + + for (const auto &[RowKey, Row] : Tab) { + Sz += + Row.bucket_count() * sizeof(void *) + + Row.size() * + sizeof( + std::tuple::value_type>); + } + return Sz; + } + [[nodiscard]] std::set cellSet() const { // Returns a set of all row key / column key / value triplets. std::set Result; @@ -162,6 +181,34 @@ template class Table { return Tab[std::move(RowKey)][std::move(ColumnKey)]; } + [[nodiscard]] V getOrDefault(ByConstRef RowKey, + ByConstRef ColumnKey) const { + auto OuterIt = Tab.find(RowKey); + if (OuterIt == Tab.end()) { + return V(); + } + auto InnerIt = OuterIt->second.find(ColumnKey); + if (InnerIt == OuterIt->second.end()) { + return V(); + } + + return InnerIt->second; + } + + [[nodiscard]] std::optional tryGet(ByConstRef RowKey, + ByConstRef ColumnKey) { + auto OuterIt = Tab.find(RowKey); + if (OuterIt == Tab.end()) { + return std::nullopt; + } + auto InnerIt = OuterIt->second.find(ColumnKey); + if (InnerIt == OuterIt->second.end()) { + return std::nullopt; + } + + return InnerIt->second; + } + [[nodiscard]] ByConstRef get(ByConstRef RowKey, ByConstRef ColumnKey) const noexcept { // Returns the value corresponding to the given row and column keys, or V() @@ -220,12 +267,26 @@ template class Table { } [[nodiscard]] const std::unordered_map> & - rowMap() const noexcept { + rowMap() const &noexcept { + // Returns a view that associates each row key with the corresponding map + // from column keys to values. + return Tab; + } + [[nodiscard]] std::unordered_map> && + rowMap() &&noexcept { + // Returns a view that associates each row key with the corresponding map + // from column keys to values. + return std::move(Tab); + } + [[nodiscard]] const std::unordered_map> & + rowMapView() const noexcept { // Returns a view that associates each row key with the corresponding map // from column keys to values. return Tab; } + void reserve(size_t Capacity) { Tab.reserve(Capacity); } + bool operator==(const Table &Other) noexcept { return Tab == Other.Tab; } diff --git a/include/phasar/Utils/TableWrappers.h b/include/phasar/Utils/TableWrappers.h new file mode 100644 index 0000000000..18accb1b17 --- /dev/null +++ b/include/phasar/Utils/TableWrappers.h @@ -0,0 +1,1037 @@ +#ifndef PHASAR_UTILS_TABLEWRAPPERS_H +#define PHASAR_UTILS_TABLEWRAPPERS_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/EmptyBaseOptimizationUtils.h" +#include "phasar/Utils/MemoryResource.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/iterator_range.h" + +#include +#include +#include +#include +#include +#include + +namespace psr { + +template +struct has_getHashCode : std::false_type {}; +template +struct has_getHashCode().getHashCode())> + : std::true_type {}; + +template +struct has_std_hash : std::false_type {}; +template +struct has_std_hash{}(std::declval))> + : std::true_type {}; + +namespace detail { +template struct DummyTransform { + ByConstRef Value; + + // 'const' needed for decltype within llvm::mapped_iterator + // NOLINTNEXTLINE(readability-const-return-type) + const std::pair operator()(ByConstRef Key) const { + return {Key, Value}; + } +}; + +template using CellVecSmallVectorTy = llvm::SmallVector; + +template struct Hasher { + size_t operator()(ByConstRef Key) const noexcept { + if constexpr (has_getHashCode::value) { + return Key.getHashCode(); + } else { + return std::hash{}(Key); + } + } +}; + +} // namespace detail + +template class UnorderedTable1d { + using Hasher = detail::Hasher; + +public: + using value_type = std::pair; + using iterator = typename std::unordered_map::iterator; + using const_iterator = + typename std::unordered_map::const_iterator; + + UnorderedTable1d() noexcept = default; + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count(Key); + } + + V &getOrCreate(K Key) { return Map[std::move(Key)]; } + + auto insert(K Key, V Value) { + return Map.try_emplace(std::move(Key), std::move(Value)); + } + + template >> + V getOr(ByConstRef Key, V Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, const V &Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, V &&Or) const = delete; + + const_iterator find(ByConstRef Key) const { return Map.find(Key); } + + void erase(ByConstRef Key) { Map.erase(Key); } + + void erase(const_iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + std::pair, V>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + std::pair, V>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.emplace_back(std::invoke(ResProj, Elem.first), Elem.second); + } + } + return Ret; + } + + void clear() { + std::unordered_map Empty{}; + swap(Empty, Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.bucket_count() * sizeof(void *) + + Map.size() * sizeof(std::tuple); + } + +private: + std::unordered_map Map; +}; + +template class UnorderedTable1d { + using Hasher = detail::Hasher; + +public: + using value_type = DummyPair; + using iterator = typename std::unordered_set>::iterator; + using const_iterator = + typename std::unordered_set>::const_iterator; + + UnorderedTable1d() noexcept = default; + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count({Key, {}}); + } + + EmptyType getOrCreate(K Key) { + Map.insert({std::move(Key), {}}); + return {}; + } + + auto insert(K Key) { return Map.insert({std::move(Key), {}}); } + auto insert(K Key, EmptyType /*Value*/) { return insert(std::move(Key)); } + + const_iterator find(ByConstRef Key) const { return Map.find({Key, {}}); } + + void erase(ByConstRef Key) { Map.erase({Key, {}}); } + + void erase(const_iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + DummyPair>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + DummyPair>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.push_back({std::invoke(ResProj, Elem.first), {}}); + } + } + return Ret; + } + + void clear() { + std::unordered_set, Hasher> Empty{}; + swap(Empty, Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.bucket_count() * sizeof(void *) + + Map.size() * sizeof(std::tuple); + } + +private: + std::unordered_set, Hasher> Map; +}; + +template class DummyUnorderedTable1d { + struct Hasher { + size_t operator()(ByConstRef Key) const noexcept { + if constexpr (has_getHashCode::value) { + return Key.getHashCode(); + } else { + return std::hash{}(Key); + } + } + }; + +public: + using value_type = std::pair; + using iterator = llvm::mapped_iterator< + typename std::unordered_set::const_iterator, + detail::DummyTransform>; + using const_iterator = iterator; + + explicit DummyUnorderedTable1d(V Value) noexcept( + std::is_nothrow_move_constructible_v) + : Value(std::move(Value)) {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count(Key); + } + + const V &getOrCreate(K Key) { + Map.insert(std::move(Key)); + return Value; + } + + template >> + V getOr(ByConstRef Key, V Or) const { + if (Map.count(Key)) { + return Value; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, const V &Or) const { + if (Map.count(Key)) { + return Value; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, V &&Or) const = delete; + + const_iterator find(ByConstRef Key) const { + return llvm::map_iterator(Map.find(Key), + detail::DummyTransform{Value}); + } + + void erase(ByConstRef Key) { Map.erase(Key); } + + void erase(iterator It) { Map.erase(It.getCurrent()); } + + auto cells() const noexcept { + return llvm::map_range(llvm::make_range(Map.begin(), Map.end()), + detail::DummyTransform{Value}); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + auto Cells = cells(); + Ret.insert(Ret.end(), Cells.begin(), Cells.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.emplace_back(Elem, Value); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + std::pair, V>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + std::pair, V>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem) == Of) { + Ret.emplace_back(std::invoke(ResProj, Elem), Value); + } + } + return Ret; + } + + void clear() { + std::unordered_set Empty{}; + swap(Empty, Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.bucket_count() * sizeof(void *) + + Map.size() * sizeof(std::tuple); + } + +private: + std::unordered_set Map; + V Value{}; +}; + +template class UnorderedSet { +public: + using value_type = T; + UnorderedSet() noexcept = default; + + void reserve(size_t Capacity) { Set.reserve(Capacity); } + + auto insert(T Val) { return Set.insert(std::move(Val)); } + + bool contains(ByConstRef Val) const noexcept { return Set.count(Val); } + + bool erase(ByConstRef Val) { return Set.erase(Val); } + + auto begin() const noexcept { return Set.begin(); } + auto end() const noexcept { return Set.end(); } + + void + clear() noexcept(std::is_nothrow_default_constructible_v) { + std::unordered_set Empty{}; + swap(Set, Empty); + } + + auto cells() const noexcept { return llvm::make_range(begin(), end()); } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Set.size()); + Ret.insert(Ret.end(), Set.begin(), Set.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Set) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy> Ret; + for (const auto &Elem : Set) { + if (std::invoke(Proj, Elem) == Of) { + Ret.push_back(std::invoke(ResProj, Elem)); + } + } + return Ret; + } + + [[nodiscard]] size_t size() const noexcept { return Set.size(); } + [[nodiscard]] bool empty() const noexcept { return Set.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Set.bucket_count() * sizeof(void *) + + Set.size() * sizeof(std::tuple); + } + +private: + struct Hasher { + size_t operator()(ByConstRef Key) const noexcept { + if constexpr (has_getHashCode::value) { + return Key.getHashCode(); + } else { + return std::hash{}(Key); + } + } + }; + + std::unordered_set Set; +}; + +template class DenseTable1d { + +public: + using value_type = std::pair; + using iterator = typename llvm::DenseMap::iterator; + using const_iterator = typename llvm::DenseMap::const_iterator; + + DenseTable1d() noexcept = default; + /// Dummy ctor for compatibility with UnorderedTable1d + template + explicit DenseTable1d(Allocator /*Alloc*/) noexcept {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count(Key); + } + + V &getOrCreate(K Key) { return Map[std::move(Key)]; } + + auto insert(K Key, V Value) { + return Map.try_emplace(std::move(Key), std::move(Value)); + } + + template >> + V getOr(ByConstRef Key, V Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, const V &Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, V &&Or) const = delete; + + const_iterator find(ByConstRef Key) const { return Map.find(Key); } + + void erase(ByConstRef Key) { Map.erase(Key); } + void erase(iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + std::pair, V>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + std::pair, V>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.emplace_back(std::invoke(ResProj, Elem.first), Elem.second); + } + } + return Ret; + } + + void clear() noexcept { + llvm::DenseMap Empty; + Empty.swap(Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.getMemorySize(); + } + +private: + llvm::DenseMap Map; +}; + +template class DenseTable1d { + +public: + using value_type = DummyPair; + using iterator = typename llvm::DenseSet>::iterator; + using const_iterator = typename llvm::DenseSet>::const_iterator; + + DenseTable1d() noexcept = default; + /// Dummy ctor for compatibility with UnorderedTable1d + template + explicit DenseTable1d(Allocator /*Alloc*/) noexcept {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count({Key, {}}); + } + + EmptyType getOrCreate(K Key) { + Map.insert({std::move(Key), {}}); + return {}; + } + + auto insert(K Key) { return Map.insert({std::move(Key), {}}); } + auto insert(K Key, EmptyType /*Value*/) { return insert(std::move(Key)); } + + const_iterator find(ByConstRef Key) const { return Map.find({Key, {}}); } + + void erase(ByConstRef Key) { Map.erase({Key, {}}); } + void erase(iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + DummyPair>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + DummyPair>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.push_back({std::invoke(ResProj, Elem.first), {}}); + } + } + return Ret; + } + + void clear() noexcept { + llvm::DenseSet> Empty; + Empty.swap(Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.getMemorySize(); + } + +private: + llvm::DenseSet> Map; +}; + +template class SmallDenseTable1d { + +public: + using container_type = llvm::SmallDenseMap; + using value_type = std::pair; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + SmallDenseTable1d() noexcept = default; + /// Dummy ctor for compatibility with UnorderedTable1d + template + explicit SmallDenseTable1d(Allocator /*Alloc*/) noexcept {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count(Key); + } + + V &getOrCreate(K Key) { return Map[std::move(Key)]; } + + auto insert(K Key, V Value) { + return Map.try_emplace(std::move(Key), std::move(Value)); + } + + template >> + V getOr(ByConstRef Key, V Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, const V &Or) const { + if (auto It = Map.find(Key); It != Map.end()) { + return It->second; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, V &&Or) const = delete; + + const_iterator find(ByConstRef Key) const { return Map.find(Key); } + + void erase(ByConstRef Key) { Map.erase(Key); } + void erase(iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + std::pair, V>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + std::pair, V>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.emplace_back(std::invoke(ResProj, Elem.first), Elem.second); + } + } + return Ret; + } + + void clear() noexcept { + container_type Empty; + Empty.swap(Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.getMemorySize(); + } + +private: + container_type Map; +}; + +template class SmallDenseTable1d { + +public: + using container_type = llvm::SmallDenseSet>; + using value_type = DummyPair; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + + SmallDenseTable1d() noexcept = default; + /// Dummy ctor for compatibility with UnorderedTable1d + template + explicit SmallDenseTable1d(Allocator /*Alloc*/) noexcept {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.count({Key, {}}); + } + + EmptyType getOrCreate(K Key) { + Map.insert({std::move(Key), {}}); + return {}; + } + + auto insert(K Key) { return Map.insert({std::move(Key), {}}); } + auto insert(K Key, EmptyType /*Value*/) { return insert(std::move(Key)); } + + const_iterator find(ByConstRef Key) const { return Map.find({Key, {}}); } + + void erase(ByConstRef Key) { Map.erase({Key, {}}); } + void erase(iterator It) { Map.erase(It); } + + auto cells() noexcept { return llvm::make_range(Map.begin(), Map.end()); } + auto cells() const noexcept { + return llvm::make_range(Map.begin(), Map.end()); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + Ret.insert(Ret.end(), Map.begin(), Map.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + DummyPair>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + DummyPair>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem.first) == Of) { + Ret.push_back({std::invoke(ResProj, Elem.first), {}}); + } + } + return Ret; + } + + void clear() noexcept { + container_type Empty; + Empty.swap(Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.getMemorySize(); + } + +private: + container_type Map; +}; + +/// A set that appears as map mapping to a constant +template class DummyDenseTable1d { + +public: + using value_type = typename std::pair; + using iterator = + typename llvm::mapped_iterator::const_iterator, + detail::DummyTransform>; + using const_iterator = iterator; + + DummyDenseTable1d() noexcept = default; + /// Dummy ctor for compatibility with UnorderedTable1d + template + explicit DummyDenseTable1d(Allocator /*Alloc*/) noexcept {} + template + explicit DummyDenseTable1d(Allocator /*Alloc*/, V Value) noexcept + : Value(std::move(Value)) {} + + void reserve(size_t Capacity) { Map.reserve(Capacity); } + + [[nodiscard]] bool contains(ByConstRef Key) const noexcept { + return Map.contains(Key); + } + + const V &getOrCreate(K Key) { + Map.insert(std::move(Key)); + return Value; + } + + template >> + V getOr(ByConstRef Key, V Or) const { + if (Map.contains(Key)) { + return Value; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, const V &Or) const { + if (Map.contains(Key)) { + return Value; + } + return Or; + } + + template >> + const V &getOr(ByConstRef Key, V &&Or) const = delete; + + const_iterator find(ByConstRef Key) const { + return llvm::map_iterator(Map.find(Key), + detail::DummyTransform{Value}); + } + + void erase(ByConstRef Key) { Map.erase(Key); } + void erase(iterator It) { Map.erase(It.getCurrent()); } + + auto cells() const noexcept { + return llvm::map_range(llvm::make_range(Map.begin(), Map.end()), + detail::DummyTransform{Value}); + } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Map.size()); + auto Cells = cells(); + Ret.insert(Ret.end(), Cells.begin(), Cells.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Map) { + if (std::invoke(Filter, Elem)) { + Ret.emplace_back(Elem, Value); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy< + std::pair, V>> + allOf(Projection Proj, ByConstRef> Of, + ResultProjection ResProj = {}) const { + detail::CellVecSmallVectorTy< + std::pair, V>> + Ret; + for (const auto &Elem : Map) { + if (std::invoke(Proj, Elem) == Of) { + Ret.emplace_back(std::invoke(ResProj, Elem), Value); + } + } + return Ret; + } + + void clear() noexcept { + llvm::DenseSet Empty; + Empty.swap(Map); + } + + [[nodiscard]] size_t size() const noexcept { return Map.size(); } + [[nodiscard]] bool empty() const noexcept { return Map.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Map.getMemorySize(); + } + + [[nodiscard]] iterator begin() const noexcept { + return llvm::map_iterator(Map.begin(), detail::DummyTransform{Value}); + } + [[nodiscard]] iterator end() const noexcept { + return llvm::map_iterator(Map.end(), detail::DummyTransform{Value}); + } + +private: + llvm::DenseSet Map; + V Value{}; +}; + +template > class DenseSet { +public: + using value_type = T; + DenseSet() noexcept = default; + template DenseSet(Allocator /*Alloc*/) noexcept {} + + void reserve(size_t Capacity) { Set.reserve(Capacity); } + + auto insert(T Val) { return Set.insert(std::move(Val)); } + + bool contains(ByConstRef Val) const noexcept { return Set.count(Val); } + + bool erase(ByConstRef Val) { return Set.erase(Val); } + + auto begin() const noexcept { return Set.begin(); } + auto end() const noexcept { return Set.end(); } + + void clear() noexcept { + llvm::DenseSet Empty; + Empty.swap(Set); + } + + auto cells() const noexcept { return llvm::make_range(begin(), end()); } + + detail::CellVecSmallVectorTy cellVec() const { + detail::CellVecSmallVectorTy Ret; + Ret.reserve(Set.size()); + Ret.insert(Ret.end(), Set.begin(), Set.end()); + return Ret; + } + + template + detail::CellVecSmallVectorTy cellVec(FilterFn Filter) const { + detail::CellVecSmallVectorTy Ret; + for (const auto &Elem : Set) { + if (std::invoke(Filter, Elem)) { + Ret.push_back(Elem); + } + } + return Ret; + } + + template + detail::CellVecSmallVectorTy> + allOf(Projection Proj, + ByConstRef> Of) const { + detail::CellVecSmallVectorTy> Ret; + for (const auto &Elem : Set) { + auto &&ProjKey = std::invoke(Proj, Elem); + if (ProjKey == Of) { + Ret.push_back(std::forward(ProjKey)); + } + } + return Ret; + } + + [[nodiscard]] size_t size() const noexcept { return Set.size(); } + [[nodiscard]] bool empty() const noexcept { return Set.empty(); } + + [[nodiscard]] size_t getApproxSizeInBytes() const noexcept { + return Set.getMemorySize(); + } + +private: + llvm::DenseSet Set; +}; + +} // namespace psr + +#endif // PHASAR_UTILS_TABLEWRAPPERS diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index f4c6736f28..7f0023af2f 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -10,9 +10,14 @@ #ifndef PHASAR_UTILS_TYPETRAITS_H #define PHASAR_UTILS_TYPETRAITS_H +#include "phasar/Utils/Macros.h" + #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" +#include "nlohmann/json.hpp" + +#include #include #include #include @@ -170,47 +175,73 @@ struct variant_idx, T> size_t, std::variant...>(type_identity{}).index()> {}; +template struct ElementType { + using IteratorTy = + std::decay_t()))>; + using type = typename std::iterator_traits::value_type; +}; + +template +struct has_isInteresting : std::false_type {}; // NOLINT +template +struct has_isInteresting< + ProblemTy, + decltype(std::declval>().isInteresting( + std::declval()))> + : std::true_type {}; + +template +struct has_llvm_dense_map_info : std::false_type {}; +template +struct has_llvm_dense_map_info< + T, std::void_t::getEmptyKey()), + decltype(llvm::DenseMapInfo::getTombstoneKey()), + decltype(llvm::DenseMapInfo::getHashValue( + std::declval())), + decltype(llvm::DenseMapInfo::isEqual(std::declval(), + std::declval()))>> + : std::true_type {}; } // namespace detail template -constexpr bool is_iterable_v = detail::is_iterable::value; // NOLINT +PSR_CONCEPT is_iterable_v = detail::is_iterable::value; // NOLINT template -constexpr bool is_iterable_over_v = // NOLINT +PSR_CONCEPT is_iterable_over_v = // NOLINT detail::is_iterable_over::value; template -constexpr bool is_pair_v = detail::is_pair::value; // NOLINT +PSR_CONCEPT is_pair_v = detail::is_pair::value; // NOLINT template -constexpr bool is_tuple_v = detail::is_tuple::value; // NOLINT +PSR_CONCEPT is_tuple_v = detail::is_tuple::value; // NOLINT template -constexpr bool is_llvm_printable_v = // NOLINT +PSR_CONCEPT is_llvm_printable_v = // NOLINT detail::is_llvm_printable::value; template -constexpr bool is_std_printable_v = // NOLINT +PSR_CONCEPT is_std_printable_v = // NOLINT detail::is_std_printable::value; template -constexpr bool is_printable_v = detail::is_printable::value; // NOLINT +PSR_CONCEPT is_printable_v = detail::is_printable::value; // NOLINT template -constexpr bool has_str_v = detail::has_str::value; // NOLINT +PSR_CONCEPT has_str_v = detail::has_str::value; // NOLINT template -constexpr bool has_adl_to_string_v = detail::has_adl_to_string::value; +PSR_CONCEPT has_adl_to_string_v = detail::has_adl_to_string::value; template -constexpr bool has_erase_iterator_v = // NOLINT +PSR_CONCEPT has_erase_iterator_v = // NOLINT detail::has_erase_iterator::value; template -constexpr bool is_std_hashable_v = detail::is_std_hashable::value; // NOLINT +PSR_CONCEPT is_std_hashable_v = detail::is_std_hashable::value; // NOLINT template -constexpr bool is_llvm_hashable_v = // NOLINT +PSR_CONCEPT is_llvm_hashable_v = // NOLINT detail::is_llvm_hashable::value; template struct is_variant : std::false_type {}; // NOLINT @@ -218,36 +249,47 @@ template struct is_variant : std::false_type {}; // NOLINT template struct is_variant> : std::true_type {}; // NOLINT -template -inline constexpr bool is_variant_v = is_variant::value; // NOLINT +template PSR_CONCEPT is_variant_v = is_variant::value; // NOLINT template // NOLINTNEXTLINE -constexpr bool is_string_like_v = std::is_convertible_v; +PSR_CONCEPT is_string_like_v = std::is_convertible_v; template