diff --git a/.clang-tidy b/.clang-tidy
index 3308a915db..d7b7d2d702 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -19,6 +19,9 @@ Checks: '-*,
-readability-identifier-length,
-readability-redundant-member-init,
-readability-use-anyofallof,
+ -readability-avoid-return-with-void-value,
+ -readability-use-std-min-max,
+ -readability-math-missing-parentheses,
cppcoreguidelines-*,
-cppcoreguidelines-avoid-non-const-global-variables,
-cppcoreguidelines-pro-bounds-array-to-pointer-decay,
@@ -36,6 +39,7 @@ Checks: '-*,
-modernize-use-trailing-return-type,
-modernize-avoid-c-arrays,
performance-*,
+ -performance-enum-size,
clang-analyzer-*
'
diff --git a/.dockerignore b/.dockerignore
index 2b31c69a2b..22da52561e 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1,7 +1,7 @@
*
-!.git
!cmake/
!config/
+!external/
!include/
!lib/
!test/
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7cf419c0e3..ea0a25eda0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,9 +15,9 @@ jobs:
fail-fast: true
matrix:
os: [ubuntu-24.04, ubuntu-24.04-arm]
- compiler: [ [clang++-19, clang-19, "clang-19 libclang-rt-19-dev clang-tools-19"] ]
+ compiler: [ [clang++-21, clang-21, "clang-21 libclang-rt-21-dev clang-tools-21"] ]
build: [ Debug, Release, DebugLibdeps, DebugCov ]
- llvm-version: [ 16, 17 ]
+ llvm-version: [ 16, "22.1" ]
include:
- build: Debug
cmake_build_type: Debug
@@ -31,7 +31,11 @@ jobs:
- build: DebugCov
cmake_build_type: Debug
flags: -DCODE_COVERAGE=ON
- extra_dependencies: llvm-19 # For coverage
+ extra_dependencies: llvm-21 # For coverage
+ - llvm-version: 16
+ llvm-major-version: 16
+ - llvm-version: "22.1"
+ llvm-major-version: 22
exclude:
- os: ubuntu-24.04-arm
build: Debug
@@ -43,7 +47,7 @@ jobs:
continue-on-error: false
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
with:
fetch-depth: 0
submodules: recursive
@@ -56,7 +60,11 @@ jobs:
- name: Install Phasar Dependencies
shell: bash
run: |
- ./utils/InstallAptDependencies.sh --llvm-version ${{ matrix.llvm-version }} --noninteractive tzdata ccache ${{ matrix.compiler[2] }} ${{ matrix.extra_dependencies }}
+ ./utils/InstallAptDependencies.sh \
+ --llvm-version ${{ matrix.llvm-major-version }} \
+ --noninteractive \
+ tzdata ccache ${{ matrix.compiler[2] }} ${{ matrix.extra_dependencies }} \
+ llvm-${{ matrix.llvm-major-version }}-tools # For lit, FileCheck
- name: Building Phasar in ${{ matrix.build }} with ${{ matrix.compiler[0] }}
env:
@@ -71,6 +79,7 @@ jobs:
-DBUILD_PHASAR_CLANG=OFF \
-DPHASAR_USE_Z3=ON \
-DPHASAR_BUILD_MODULES=ON \
+ -DPHASAR_TARGET_ARCH="" \
-DPHASAR_LLVM_VERSION=${{ matrix.llvm-version }} \
${{ matrix.flags }} \
-G Ninja
@@ -79,7 +88,7 @@ jobs:
- name: Run Unittests
shell: bash
run: |
- cmake --build ./build --target check-phasar-unittests
+ cmake --build ./build --target check-phasar-unittests check-phasar-cli
- name: Install PhASAR and Build Examples
if: matrix.build == 'DebugLibdeps' # Circumvent conflicting ASAn flags
@@ -91,7 +100,7 @@ jobs:
cmake -DCMAKE_INSTALL_PREFIX=./INSTALL -P ./build/cmake_install.cmake
PHASAR_ROOT_DIR=$(pwd)
cd ./examples/how-to
- cmake -S . -B build -Dphasar_ROOT="$PHASAR_ROOT_DIR/INSTALL"
+ cmake -S . -B build -G Ninja -Dphasar_ROOT="$PHASAR_ROOT_DIR/INSTALL"
cmake --build ./build --target run_sample_programs
- name: Check test coverage and generate HTML report
@@ -102,7 +111,7 @@ jobs:
- name: Upload coverage HTML report artifact
if: matrix.build == 'DebugCov'
- uses: actions/upload-artifact@v4
+ uses: actions/upload-artifact@v7
with:
name: phasar-coverage-report-llvm-${{ matrix.llvm-version }}
path: ./build/ccov/all-merged/
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
index 702184a4f4..495ec24474 100644
--- a/.github/workflows/docker.yml
+++ b/.github/workflows/docker.yml
@@ -18,7 +18,10 @@ jobs:
contents: read
steps:
- name: Check out the repo
- uses: actions/checkout@v4
+ uses: actions/checkout@v7
+ with:
+ fetch-depth: 0
+ submodules: recursive
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to the Container registry
diff --git a/.gitignore b/.gitignore
index 2695fef470..7af933a2c1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -142,3 +142,6 @@ unittests/PhasarLLVM/ifdside/Problems/IFDSTaintAnalysisTest
unittests/PhasarLLVM/Pointer/LLVMTypeHierarchyTest
unittests/Utils/LLVMShorthandsTest
unittests/Utils/PAMMTest
+
+# LLVM lit test times files
+*.lit_test_times.txt
diff --git a/.gitmodules b/.gitmodules
index 350885ac54..4afb3bad23 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -5,3 +5,6 @@
[submodule "external/json-schema-validator"]
path = external/json-schema-validator
url = https://github.com/pboettch/json-schema-validator.git
+[submodule "external/CRoaring"]
+ path = external/CRoaring
+ url = https://github.com/fabianbs96/CRoaring.git
diff --git a/BUILD.md b/BUILD.md
index e8c026e721..4439bc0956 100644
--- a/BUILD.md
+++ b/BUILD.md
@@ -4,7 +4,9 @@
It is recommended to compile PhASAR yourself in order to get the full C++ experience and to have full control over the build mode.
However, you may also want to try out one of the pre-built versions of PhASAR or the Docker container.
-As a shortcut for the very first PhASAR build on your system, you can use our [bootstrap](./bootstrap.sh) script.
+As a shortcut for the very first PhASAR build on your system, you can use our [bootstrap](./bootstrap.sh) script. It installs required dependencies and builds the libraries.
+**For subsequent builds**, see [Compiling PhASAR](#compiling-phasar-if-not-already-done-using-the-bootstrap-script).
+
Please note that you must have python installed for the script to work properly.
```bash
@@ -19,11 +21,22 @@ Note: If you want to do changes within PhASAR, it is recommended to build it in
The bootstrap script may ask for superuser permissions (to install the dependencies); however it is not recommended to start the whole script with `sudo`.
-For subsequent builds, see [Compiling PhASAR](#compiling-phasar-if-not-already-done-using-the-installation-scripts).
+### Requirements
+
+PhASAR requires the following system dependencies:
+- LLVM >=16
+ - We have tested PhASAR only with LLVM versions built with RTTI (runtime-type-info) enabled. Usually, this is no problem as the `llvm-dev` apt package ships with RTTI by default, but may be relevant if you wish to build LLVM from source.
+- C++20 compatible compiler, *we recommend clang >=20*
+- cmake >=3.23
+- some build system, *we recommend ninja*
+- depending on your LLVM configuration, you may need `zlib1g-dev`, `libzstd-dev`
+- *optional*: SVF >= 3.1, if you want to use SVF's points-to analyses from within PhASAR (`-DPHASAR_USE_SVF=ON` in cmake)
+
+If you are on a Debian-based system, you may use `./utils/InstallAptDependencies.sh` to install all required dependencies. If using the `bootstrap.sh` script, it does that for you.
### Compiling PhASAR (if not already done using the bootstrap script)
-Set the system's variables for the C and C++ compiler to clang:
+Set the system's variables for the C and C++ compiler to clang (gcc should also work, but we strongly recommend clang):
```bash
export CC=/usr/local/bin/clang
@@ -45,21 +58,31 @@ utils/init-submodules-release.sh
Navigate into the PhASAR directory. The following commands will do the job and compile the PhASAR framework:
```bash
-mkdir build
+mkdir -p build
cd build/
-cmake -G Ninja -DCMAKE_BUILD_TYPE=Release ..
+cmake -G Ninja -DCMAKE_BUILD_TYPE=Release .. # ninja is not required, but recommended for
+ # build-performance
ninja -j $(nproc) # or use a different number of cores to compile it
sudo ninja install # only if you wish to install PhASAR system wide
```
+To uninstall PhASAR from the same install location, invoke the `uninstall` target
+from the build directory:
+
+```bash
+sudo ninja uninstall
+```
+
When you have used the `bootstrap.sh` script to install PhASAR, the above steps are already done.
Use them as a reference if you wish to modify PhASAR and recompile it.
-After compilation using cmake the following two binaries can be found in the build/tools directory:
+After compilation using cmake the following two binaries can be found in the `build/tools` directory:
+ `phasar-cli` - the PhASAR command-line tool (previously called `phasar-llvm`) that provides access to analyses that are already implemented within PhASAR. Use this if you don't want to build an own tool on top of PhASAR.
+ `myphasartool` - an example tool that shows how tools can be build on top of PhASAR
+Also, find the `libphasar.a` or `libphasar.so` library in `build/lib/`.
+
Please be careful and check if errors occur during the compilation.
When using CMake to compile PhASAR the following optional parameters can be used:
@@ -69,18 +92,20 @@ When using CMake to compile PhASAR the following optional parameters can be used
| **BUILD_SHARED_LIBS** : BOOL | Build shared libraries -- Not recommended anymore. You may want to use PHASAR_BUILD_DYNLIB instead (default is OFF) |
| **PHASAR_BUILD_DYNLIB** : BOOL | Build one fat shared library (default is OFF) |
| **CMAKE_BUILD_TYPE** : STRING | Build PhASAR in 'Debug', 'RelWithDebInfo' or 'Release' mode (default is 'Debug') |
-| **CMAKE_INSTALL_PREFIX** : PATH | Path where PhASAR will be installed if "ninja install” is invoked or the “install” target is built (default is /usr/local/phasar) |
+| **CMAKE_INSTALL_PREFIX** : PATH | Path where PhASAR will be installed if "ninja install” is invoked or the “install” target is built (default is /usr/local/phasar or whatever is your system's default install location) |
| **PHASAR_CUSTOM_CONFIG_INSTALL_DIR** : PATH | If set, customizes the directory, where configuration files for PhASAR are installed (default is /usr/local/.phasar-config)|
-| **PHASAR_ENABLE_DYNAMIC_LOG** : BOOL|Makes it possible to switch the logger on and off at runtime (default is ON)|
-| **PHASAR_BUILD_DOC** : BOOL | Build PhASAR documentation (default is OFF) |
-| **PHASAR_BUILD_UNITTESTS** : BOOL | Build PhASAR unit tests (default is ON) |
+| **PHASAR_ENABLE_DYNAMIC_LOG** : BOOL|Makes it possible to switch the logger on and off at runtime, otherwise all log-statements are compiled-out (default is ON)|
+| **PHASAR_BUILD_DOC** : BOOL | Build PhASAR's Doxygen documentation (default is OFF) |
| **PHASAR_BUILD_IR** : BOOL | Build PhASAR IR (required for running the unit tests) (default is ON) |
+| **PHASAR_BUILD_UNITTESTS** : BOOL | Build PhASAR unit tests. Requires `PHASAR_BUILD_IR` (default is ON) |
+| **PHASAR_ENABLE_INTEGRATIONTESTS** : BOOL | Build PhASAR LIT integration tests. Requires `PHASAR_BUILD_IR` (default is ON) |
| **PHASAR_BUILD_OPENSSL_TS_UNITTESTS** : BOOL | Build PhASAR unit tests that require OpenSSL (default is OFF) |
| **PHASAR_ENABLE_PAMM** : STRING | Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is Off) |
| **PHASAR_ENABLE_PIC** : BOOL | Build Position-Independed Code (default is ON) |
| **PHASAR_ENABLE_WARNINGS** : BOOL | Enable compiler warnings (default is ON) |
| **CMAKE_CXX_STANDARD** : INT|Adapt the used C++ standard (minimum required is 20)|
-| **PHASAR_LLVM_VERSION** : INT|The LLVM major-version to use. Can be 16 or 17 (default is 16)|
+| **PHASAR_LLVM_VERSION** : VERSION|The LLVM major-version to use. Can be between 16 and 22.1 (default is 16)|
+| **PHASAR_USE_SVF** : BOOL| Enable using SVF points-to info from PhASAR. See [Use PhASAR with SVF](https://github.com/secure-software-engineering/phasar/wiki/FAQ#how-to-use-phasar-with-svf). Requires SVF installed (default OFF) |
You can use these parameters either directly or modify the installer-script `bootstrap.sh`
@@ -88,15 +113,30 @@ You can use these parameters either directly or modify the installer-script `boo
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
+### Using PhASAR with Conan v2
-To test if everything works as expected please run the following command:
+To export the recipe and dependencies, execute from the repo root:
-`$ phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest`
+- `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)
-You can find the `phasar-cli` tool in the build-tree under `tools/phasar-cli`.
+If you just want to use phasar-cli:
-If you obtain output other than a segmentation fault or an exception terminating the program abnormally everything works as expected.
+- `conan install --tool-requires phasar/... --build=missing -of .`
+- `source conanbuild.sh`
+- `phasar-cli --help`
+
+### Running a Test Solver
+
+To test if everything works as expected please run the following command (from the `build/` folder):
+
+`$ ./tools/phasar-cli/phasar-cli -m test/llvm_test_code/basic/module_cpp.ll -D ifds-solvertest --emit-raw-results`
+
+The output should show that the `@zero_value` data-flow fact holds at every LLVM instruction in the module.
+Especially, there should be no segmentation fault, exception, or other non-zero exit condition.
### Building PhASAR on a MacOS System
@@ -107,7 +147,7 @@ Now you can just attach your docker container to VS Code or any other IDE, which
## 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.
+However, **you do not need to install PhASAR in order to use it**.
### Installing PhASAR on an Ubuntu System
@@ -129,3 +169,9 @@ If You have already built phasar, you can just invoke
```bash
sudo ninja install
```
+
+To uninstall PhASAR again, invoke the uninstall target from the same build directory:
+
+```bash
+sudo ninja uninstall
+```
diff --git a/BreakingChanges.md b/BreakingChanges.md
index f113d64b3c..c8805cfbb8 100644
--- a/BreakingChanges.md
+++ b/BreakingChanges.md
@@ -2,7 +2,18 @@
## development HEAD
-*None*
+- *None*
+
+## v2610
+
+- Existing IFDS/IDE problems provided by PhASAR do no longer inherit from `IFDSTabulationProblem`/`IDETabulationProblem`. The solvers can now deal with them without polymorphic inheritance hierarchy. However, **you cannot cast an analysis problem to one of these base classes anymore**. If that's required (unlikely), wrap your problem in a `LegacyIDEProblemWrapper`.
+ - Although the IFDS/IDETabulationProblem classes still work, new IFDS/IDE analysis problems should conform to the `IFDSProblem`/`IDEProblem` concepts instead. Use `IfdsIdeProblemMixin` as helper for that.
+- `IDESolver` and `IFDSSolver` now take analysis problems by pointer instead of by reference to better document that they capture the address (without taking ownership).
+- `FlowFunctionPtrType` is now a move-only type.
+- Removed the CRTP-template `ProjectIRDBBase`. Use the concept `ProjectIRDB` instead.
+- Removed `EdgeFunction::equals`. Use `operator==` instead.
+- Removed ctor-overloads of `LLVMProjectIRDB` that use the `EnableOpaquePointers` parameter. LLVM is removing support for non-opaque pointers. Use the other ctors instead.
+- Removed type-trait `has_getAsJson`, as it is not used in our codebase anymore. We switched from `getAsJson` to `printAsJson` a while ago.
## v2604
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0bb9701c17..2171b80bdb 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,4 +1,4 @@
-cmake_minimum_required (VERSION 3.14...3.28)
+cmake_minimum_required (VERSION 3.23...4.2) # 3.23+ needed by FILE_SET (https://cmake.org/cmake/help/latest/command/target_sources.html#file-sets)
# Avoid IPO/LTO Warnings:
cmake_policy(SET CMP0069 NEW)
@@ -8,11 +8,9 @@ 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 overwriting cache variables of external projects from this CMakeLists file
+cmake_policy(SET CMP0126 NEW)
+set(CMAKE_POLICY_DEFAULT_CMP0126 NEW)
# Allow portable use of CMAKE_VISIBILITY_INLINES_HIDDEN not only for shared libraries
cmake_policy(SET CMP0063 NEW)
@@ -23,7 +21,7 @@ if (DEFINED LLVM_MAIN_SRC_DIR)
set(PHASAR_IN_TREE 1)
endif()
-set(PHASAR_VERSION 2604)
+set(PHASAR_VERSION 2610)
if (NOT PHASAR_IN_TREE)
project (phasar
LANGUAGES C CXX
@@ -74,7 +72,9 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE)
string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
+string(APPEND CMAKE_C_FLAGS_DEBUG " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer")
+string(APPEND CMAKE_C_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELEASE "")
option(CMAKE_VISIBILITY_INLINES_HIDDEN "Hide inlined functions from the DSO table (default ON)" ON)
@@ -123,6 +123,7 @@ if (NOT "${PHASAR_TARGET_ARCH_INTERNAL}" STREQUAL "")
if (MARCH_SUPPORTED)
message(STATUS "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' enabled")
string(APPEND CMAKE_CXX_FLAGS_RELEASE " -march=${PHASAR_TARGET_ARCH_INTERNAL}")
+ string(APPEND CMAKE_C_FLAGS_RELEASE " -march=${PHASAR_TARGET_ARCH_INTERNAL}")
else()
message(WARNING "Target architecture '${PHASAR_TARGET_ARCH_INTERNAL}' not supported. Fallback to generic build")
endif()
@@ -158,6 +159,7 @@ if(PHASAR_ALLOW_LTO_IN_RELEASE_BUILD)
endif()
option(PHASAR_BUILD_UNITTESTS "Build all tests (default is ON)" ${PHASAR_BUILD_OPTIONAL_TARGETS_DEFAULT})
+option(PHASAR_ENABLE_INTEGRATIONTESTS "Enable LIT integration 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)
@@ -197,18 +199,16 @@ if (PHASAR_ENABLE_PIC)
endif (PHASAR_ENABLE_PIC)
# PAMM
-if (NOT PHASAR_ENABLE_PAMM)
- set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')" FORCE)
- set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full")
-endif()
-if(PHASAR_ENABLE_PAMM STREQUAL "Core" AND NOT PHASAR_BUILD_UNITTESTS)
+set(PHASAR_ENABLE_PAMM "Off" CACHE STRING "Enable the performance measurement mechanism ('Off', 'Core' or 'Full', default is 'Off')")
+set_property(CACHE PHASAR_ENABLE_PAMM PROPERTY STRINGS "Off" "Core" "Full")
+option(PHASAR_THREAD_SAFE_PAMM "Use a thread-safe version of PAMM, if PAMM is enabled" ON)
+
+if(PHASAR_ENABLE_PAMM STREQUAL "Core")
set(PAMM_CORE ON)
message(STATUS "PAMM metric severity level: Core")
-elseif(PHASAR_ENABLE_PAMM STREQUAL "Full" AND NOT PHASAR_BUILD_UNITTESTS)
+elseif(PHASAR_ENABLE_PAMM STREQUAL "Full")
set(PAMM_FULL ON)
message(STATUS "PAMM metric severity level: Full")
-elseif(PHASAR_BUILD_UNITTESTS AND (PHASAR_ENABLE_PAMM STREQUAL "Core" OR PHASAR_ENABLE_PAMM STREQUAL "Full"))
- message(WARNING "PAMM metric severity level: Off (due to unittests)")
else()
message(STATUS "PAMM metric severity level: Off")
endif()
@@ -225,16 +225,6 @@ endif()
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")
- message(STATUS "Detected CMAKE_INSTALL_LIBDIR that deviates from 'lib': ${CMAKE_INSTALL_LIBDIR}. Add ${CMAKE_INSTALL_PREFIX}/lib to the RPATH as json-schema-validator needs it")
- list(APPEND CMAKE_INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib)
- endif()
-
- set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
-
# Export set
set(PHASAR_DEPS_EXPORT_SET PhasarDepsExports)
else()
@@ -280,11 +270,15 @@ include(add_nlohmann_json)
add_nlohmann_json()
add_json_schema_validator()
+# TBB
+find_package(TBB QUIET) # needed by GCC
+set(PHASAR_USE_TBB ${TBB_FOUND})
+
# Coverage
if (CODE_COVERAGE)
set(CODE_COVERAGE_SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/code-coverage.cmake")
- file(DOWNLOAD "https://raw.githubusercontent.com/StableCoder/cmake-scripts/refs/tags/25.08/code-coverage.cmake" "${CODE_COVERAGE_SCRIPT}"
- EXPECTED_HASH SHA256=cad4dd2bdf944f77627687e0298debc8341bfdeffaf949937f319c7c4ff3fdaf
+ file(DOWNLOAD "https://raw.githubusercontent.com/StableCoder/cmake-scripts/c012a75c255e57523c9e2faacd86356439318542/code-coverage.cmake" "${CODE_COVERAGE_SCRIPT}"
+ EXPECTED_HASH SHA256=21caeb58c8270b00a15c58b06cab72255467f9917d07df5a5042259bbcdfd1ab
TLS_VERIFY ON
STATUS CODE_COVERAGE_SCRIPT_DOWNLOAD_STATUS
)
@@ -339,6 +333,15 @@ set(PHASAR_LLVM_VERSION 16 CACHE STRING "The LLVM major-version that PhASAR shou
include(add_llvm)
add_llvm()
+# Roaring
+
+find_package(roaring QUIET CONFIG)
+if(NOT TARGET roaring::roaring)
+ set(ENABLE_ROARING_TESTS OFF)
+ add_subdirectory(external/CRoaring)
+ set(PHASAR_PROVIDE_CROARING ON)
+endif()
+
# SVF
option(PHASAR_USE_SVF "Use SVF for more options in alias analysis (default is OFF)" OFF)
if(PHASAR_USE_SVF)
@@ -425,6 +428,9 @@ target_sources(phasar_interface INTERFACE
BASE_DIRS "${PHASAR_SRC_DIR}/include" "${PHASAR_BINARY_DIR}/include"
FILES ${PHASAR_PUBLIC_HEADERS} "${PHASAR_BINARY_DIR}/include/phasar/Config/phasar-config.h"
)
+if (TARGET TBB::tbb)
+ target_link_libraries(phasar_interface INTERFACE TBB::tbb) # needed by GCC
+endif()
# Some preprocessor symbols that need to be available in phasar sources, but should not be installed
add_cxx_compile_definitions(PHASAR_SRC_DIR="${CMAKE_SOURCE_DIR}")
@@ -538,6 +544,20 @@ install(
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/phasar"
)
+if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT TARGET uninstall)
+ configure_file(
+ "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
+ "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+ @ONLY
+ )
+
+ add_custom_target(uninstall
+ COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
+ COMMENT "Uninstalling PhASAR"
+ VERBATIM
+ )
+endif()
+
# If the Phasar shared object libraries are not installed into a system folder
# the so libs must be added manually to the linker search path and the linker
# config must be updated as follows:
diff --git a/Config.cmake.in b/Config.cmake.in
index 085a277031..da9ffd1ffa 100644
--- a/Config.cmake.in
+++ b/Config.cmake.in
@@ -15,6 +15,18 @@ 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_BUILD_MODULES @PHASAR_BUILD_MODULES@)
+set(PHASAR_PROVIDE_CROARING @PHASAR_PROVIDE_CROARING@)
+
+if (PHASAR_PROVIDE_CROARING)
+ # TODO: Is that path portable?
+ include("${CMAKE_CURRENT_LIST_DIR}/../roaring/roaring-targets.cmake")
+else()
+ find_dependency(roaring REQUIRED CONFIG)
+endif()
+
+if (@PHASAR_USE_TBB@)
+ find_dependency(TBB)
+endif()
if (PHASAR_USE_Z3)
find_dependency(Z3 REQUIRED)
diff --git a/Dockerfile b/Dockerfile
index ae8b16d24f..dada676b79 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -3,10 +3,10 @@ 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 clang-tools-19
+ ./InstallAptDependencies.sh --noninteractive tzdata clang-20 libclang-rt-20-dev clang-tools-20 llvm-16-tools llvm-20-tools
-ENV CC=/usr/bin/clang-19 \
- CXX=/usr/bin/clang++-19
+ENV CC=/usr/bin/clang-20 \
+ CXX=/usr/bin/clang++-20
FROM build
@@ -14,7 +14,10 @@ ARG RUN_TESTS=OFF
RUN --mount=type=bind,source=.,target=/usr/src/phasar,rw \
set -eux; \
cd /usr/src/phasar; \
- git submodule update --init; \
+ if find external -mindepth 1 -maxdepth 1 -type d -empty | grep -q .; then \
+ echo "external/* submodules are not checked out; run 'git submodule update --init --recursive' on the host before building"; \
+ exit 1; \
+ fi; \
cmake -S . -B cmake-build/Release \
-DCMAKE_BUILD_TYPE=Release \
-DPHASAR_TARGET_ARCH="" \
@@ -23,10 +26,11 @@ RUN --mount=type=bind,source=.,target=/usr/src/phasar,rw \
-DPHASAR_USE_Z3=ON \
-DPHASAR_BUILD_UNITTESTS=$RUN_TESTS \
-DPHASAR_BUILD_IR=$RUN_TESTS \
+ -DPHASAR_ENABLE_INTEGRATIONTESTS=$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; \
+ [ "${RUN_TESTS}" = "OFF" ] || cmake --build ./cmake-build/Release --target check-phasar-unittests check-phasar-cli ; \
phasar-cli --version
ENTRYPOINT [ "phasar-cli" ]
diff --git a/LICENSE.txt b/LICENSE.txt
index 6b44aff4be..ed8ad367db 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -208,3 +208,38 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+
+
+CRoaring
+-------------------------------------------------------------------------------
+The CRoaring project is under a dual license (Apache/MIT).
+Users of the library may choose one or the other license.
+---
+
+MIT License
+
+Copyright 2016-2022 The CRoaring authors
+
+Permission is hereby granted, free of charge, to any
+person obtaining a copy of this software and associated
+documentation files (the "Software"), to deal in the
+Software without restriction, including without
+limitation the rights to use, copy, modify, merge,
+publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software
+is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice
+shall be included in all copies or substantial portions
+of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
+ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
+TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
+PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
+SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
+IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
index db217e8d39..ea2b1673f0 100644
--- a/README.md
+++ b/README.md
@@ -8,53 +8,59 @@
## What is PhASAR?
-PhASAR is a LLVM-based static analysis framework written in C++. It allows users
-to specify arbitrary data-flow problems which are then solved in a
-fully-automated manner on the specified LLVM IR target code. Computing points-to
-information, call-graph(s), etc. is done by the framework, thus you can focus on
-what matters.
+PhASAR is a LLVM-based static analysis framework written in C++.
+It allows users to specify arbitrary data-flow problems which are then solved in a fully-automated manner on the specified LLVM IR target code.
+Computing points-to information, call-graph(s), etc. is done by the framework, thus you can focus on what matters.
You can find available literature on PhASAR [here](https://github.com/secure-software-engineering/phasar/wiki/Useful-Literature#papers-on-phasar).
-### How do I get started with PhASAR?
+### Key Features
-We have some documentation on PhASAR in our [***Wiki***](https://github.com/secure-software-engineering/phasar/wiki). You probably would like to read
-this README first.
-
-Please also have a look on PhASAR's project directory and notice the project directory [examples](./examples/) as well as the custom tool `tools/example-tool/myphasartool.cpp`.
+- **IFDS/IDE solver**: Interprocedural data-flow solvers based on the IFDS/IDE algorithm
+- **MonoIFDS solver**: High-performance data-flow solver for *bottom-up* IFDS analyses
+- **Sparse analysis**: SparseIFDS/SparseIDE for improved performance
+- **Call-graph construction**: Several algorithms (CHA, RTA, VTA, alias-based)
+- **Type-hierarchy construction**: Extract high-level C++ type information from LLVM IR
+- **Points-to/alias infrastructure**: High-performance alias analyses for LLVM IR. Integration with state-of-the-art alias/points-to information from SVF supported
+- **Interprocedural CFG (ICFG)**: Connecting control-flow with call-graph information
+- **Path-tracking**: Improve results-reporting by reconstruct concrete data-flow paths from IFDS/IDE results
+- **Monotone solver**: Simple intra-procedural analysis engine, based on Monotone Frameworks
+- **Taint analysis**: Infrastructure for taint-configuration & IFDS/IDE-based taint analysis
+- **Modern C++20 API**: Modular, easy-to use interfaces, also for non C++ experts
-You can find PhASAR's API reference [here](https://secure-software-engineering.github.io/phasar/).
+#### Experimental Features
+- **WPDS solver**: Data-flow solver based on weighted pushdown systems. Can solve any IFDS/IDE problem; supports sparsification
+- **DFI solver**: Data-flow solver based on DFS-interval reachability. Can solve any IFDS problem; supports sparsification
-## Secure Software Engineering Group
+### How do I get started with PhASAR?
-PhASAR is primarily developed and maintained by the Secure Software Engineering Group at Heinz Nixdorf Institute (University of Paderborn) and Fraunhofer IEM.
+We have some documentation on PhASAR in our [***Wiki***](https://github.com/secure-software-engineering/phasar/wiki). You probably would like to read this README first.
-PhASAR was initially developed by Philipp Dominik Schubert (@pdschubert)().
+Please also have a look at PhASAR's project directory, in particular the [examples](./examples/) directory and the custom tool `tools/example-tool/myphasartool.cpp`.
-Currently, PhASAR is maintained by
-- Fabian Schiebel (@fabianbs96)()
-- Sriteja Kummita (@sritejakv)
-- Lucas Briese (@jusito)
-- Martin Mory (@MMory)()
-- *others*
+You can find PhASAR's API reference [here](https://secure-software-engineering.github.io/phasar/).
+
+## Requirements
-## Required Version of the C++ Standard
+### C++ Standard
-**NEW**: PhASAR requires at least C++-20.
+PhASAR requires at least C++20.
PhASAR supports C++20 modules as an experimental feature.
-## Currently Supported Version of LLVM
+### LLVM Version
-**NEW**: PhASAR is currently set up to support **LLVM-16 and 17**, using LLVM 16 by default.
-Specify the `PHASAR_LLVM_VERSION` cmake-variable to change the LLVM version to use.
+PhASAR supports LLVM versions **between LLVM-16 and LLVM-22.1**, using LLVM-16 by default.
+We actively test PhASAR with LLVM-16 and LLVM-22.1, so if something does not work, try these versions instead.
+Specify the `PHASAR_LLVM_VERSION` cmake variable to change the LLVM version to use.
+> [!NOTE]
+> Since v18, LLVM uses minor versions, so use `-DPHASAR_LLVM_VERSION=17` for LLVM-17 but `-DPHASAR_LLVM_VERSION=18.1` for LLVM-18
## Breaking Changes
-To keep PhASAR in a state that it is well suited for state-of-the-art research in static analysis, as well as for productive use, we have to make breaking changes. Please refer to [Breaking Changes](./BreakingChanges.md) for detailed information on what was broken recently and how to migrate.
+To keep PhASAR in a state that is well suited for state-of-the-art research in static analysis, as well as for productive use, we have to make breaking changes. Please refer to [Breaking Changes](./BreakingChanges.md) for detailed information on what was broken recently and how to migrate.
## Building PhASAR
@@ -62,12 +68,39 @@ Please refer to [BUILD.md](./BUILD.md) for instructions on how to build PhASAR.
## How to use PhASAR?
-We recomment using phasar as a library with `cmake` or `conan`.
+The following example shows how to use PhASAR's core concepts of IFDS/IDE analysis, alias analysis, type-hierarchy, call-graph, and taint analysis:
-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.
+```cpp
+#include "phasar.h"
-Otherwise, we recommend adding PhASAR as a git submodule to your repository.
-In this case, just `add_subdirectory` the phasar submodule directory within your `CMakeLists.txt`.
+// Load the target LLVM IR
+auto IRDB = psr::LLVMProjectIRDB::loadOrExit("target.ll");
+
+// Build alias information, a type-hierarchy, and a taint configuration
+// (sources/sinks can come from IR annotations, a JSON file, or callbacks)
+psr::LLVMAliasSet AS(&IRDB);
+psr::DIBasedTypeHierarchy TH(IRDB);
+psr::LLVMTaintConfig TC(IRDB);
+
+// Build the interprocedural CFG using VTA call-graph construction
+psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::VTA,
+ {"main"}, &TH, &AS);
+
+// Instantiate and solve the taint analysis
+psr::IFDSTaintAnalysis Problem(&IRDB, &AS, &TC, {"main"});
+psr::solveIFDSProblem(Problem, ICFG);
+
+// Inspect detected leaks
+for (const auto &[Inst, Facts] : Problem.Leaks) {
+ llvm::outs() << "Leak at: " << psr::llvmIRToString(Inst) << '\n';
+}
+```
+
+For more examples, including how to write a custom analysis, see [examples/how-to/](./examples/how-to/).
+
+### Integrating PhASAR into your build
+
+We recommend using PhASAR as a library with `cmake`, using `FetchContent` or as git submodule.
Assuming you have checked out phasar in `external/phasar`, the phasar-related cmake commands may look like this:
@@ -84,41 +117,28 @@ target_link_libraries(yourphasartool
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)
+For more information please consult our [PhASAR wiki pages](https://github.com/secure-software-engineering/phasar/wiki).
-If you just want to use phasar-cli:
-- `conan install --tool-requires phasar/... --build=missing -of .`
-- `source conanbuild.sh`
-- `phasar-cli --help`
+If you have PhASAR *installed*, [Use-PhASAR-as-a-library](https://github.com/secure-software-engineering/phasar/wiki/Using-Phasar-as-a-Library) may be a good start.
-## Please help us to improve PhASAR
+## Contributing
-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).
+You are very welcome to contribute to the PhASAR project.
+Just raise an issue or a pull request on GitHub.
-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!
+For details see [Contributing to PhASAR](https://github.com/secure-software-engineering/phasar/wiki/Contributing-to-PhASAR) and [Coding Conventions](https://github.com/secure-software-engineering/phasar/wiki/Coding-Conventions).
-### Installing PhASAR's Git pre-commit hook
+## Secure Software Engineering Group
-You are very much welcome to contribute to the PhASAR project.
-Please make sure that you install our pre-commit hook that ensures your commit adheres to the most important coding rules of the PhASAR project.
-For more details please consult [Coding Conventions](https://github.com/secure-software-engineering/phasar/wiki/Coding-Conventions) and [Contributing to PhASAR](https://github.com/secure-software-engineering/phasar/wiki/Contributing-to-PhASAR).
+PhASAR is primarily developed and maintained by the Secure Software Engineering Group at Heinz Nixdorf Institute (University of Paderborn) and Fraunhofer IEM.
-To install the pre-commit hook, please run the following commands in PhASAR's root directory:
+PhASAR was initially developed by Philipp Dominik Schubert (@pdschubert)().
-```bash
-pip install pre-commit
-pre-commit install
-```
+Currently, PhASAR is maintained by
-Thanks. And have fun with the project.
+- Fabian Schiebel (@fabianbs96)()
+- Sriteja Kummita (@sritejakv)
+- Lucas Briese (@jusito)
+- Martin Mory (@MMory)()
+- *others*
diff --git a/cmake/add_llvm.cmake b/cmake/add_llvm.cmake
index 1cbc9cd637..55fc6e1edb 100644
--- a/cmake/add_llvm.cmake
+++ b/cmake/add_llvm.cmake
@@ -48,7 +48,7 @@ macro(add_llvm)
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")
+ message(WARNING "PhASAR has been tested only with LLVM versions built with RTTI. You are trying to link an LLVM version *without* RTTI. Do at your own risk.")
endif()
endmacro()
diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in
new file mode 100644
index 0000000000..941910d13b
--- /dev/null
+++ b/cmake/cmake_uninstall.cmake.in
@@ -0,0 +1,81 @@
+set(PHASAR_INSTALL_MANIFEST "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
+
+if (NOT EXISTS "${PHASAR_INSTALL_MANIFEST}")
+ message(FATAL_ERROR
+ "Cannot find install manifest: ${PHASAR_INSTALL_MANIFEST}. "
+ "Run the install target before uninstalling."
+ )
+endif()
+
+file(READ "${PHASAR_INSTALL_MANIFEST}" PHASAR_INSTALLED_FILES)
+string(REGEX REPLACE "\n" ";" PHASAR_INSTALLED_FILES "${PHASAR_INSTALLED_FILES}")
+
+foreach (PHASAR_INSTALLED_FILE IN LISTS PHASAR_INSTALLED_FILES)
+ if ("${PHASAR_INSTALLED_FILE}" STREQUAL "")
+ continue()
+ endif()
+
+ set(PHASAR_UNINSTALL_FILE "$ENV{DESTDIR}${PHASAR_INSTALLED_FILE}")
+
+ if (EXISTS "${PHASAR_UNINSTALL_FILE}" OR IS_SYMLINK "${PHASAR_UNINSTALL_FILE}")
+ message(STATUS "Removing ${PHASAR_UNINSTALL_FILE}")
+ execute_process(
+ COMMAND "@CMAKE_COMMAND@" -E remove -f "${PHASAR_UNINSTALL_FILE}"
+ RESULT_VARIABLE PHASAR_REMOVE_RESULT
+ )
+
+ if (NOT "${PHASAR_REMOVE_RESULT}" STREQUAL "0")
+ message(FATAL_ERROR "Failed to remove ${PHASAR_UNINSTALL_FILE}")
+ endif()
+ else()
+ message(STATUS "File does not exist: ${PHASAR_UNINSTALL_FILE}")
+ endif()
+endforeach()
+
+function(phasar_remove_empty_directories PHASAR_DIRECTORY)
+ if (NOT IS_DIRECTORY "${PHASAR_DIRECTORY}" OR IS_SYMLINK "${PHASAR_DIRECTORY}")
+ return()
+ endif()
+
+ file(GLOB PHASAR_DIRECTORY_CHILDREN
+ LIST_DIRECTORIES true
+ "${PHASAR_DIRECTORY}/*"
+ "${PHASAR_DIRECTORY}/.[!.]*"
+ "${PHASAR_DIRECTORY}/..?*"
+ )
+
+ foreach (PHASAR_CHILD IN LISTS PHASAR_DIRECTORY_CHILDREN)
+ phasar_remove_empty_directories("${PHASAR_CHILD}")
+ endforeach()
+
+ file(GLOB PHASAR_REMAINING_CHILDREN
+ LIST_DIRECTORIES true
+ "${PHASAR_DIRECTORY}/*"
+ "${PHASAR_DIRECTORY}/.[!.]*"
+ "${PHASAR_DIRECTORY}/..?*"
+ )
+
+ if (NOT PHASAR_REMAINING_CHILDREN)
+ message(STATUS "Removing empty directory ${PHASAR_DIRECTORY}")
+ file(REMOVE_RECURSE "${PHASAR_DIRECTORY}")
+ else()
+ message(STATUS "Not removing non-empty directory ${PHASAR_DIRECTORY}")
+ endif()
+endfunction()
+
+set(PHASAR_OWNED_INSTALL_DIRECTORIES
+ "@CMAKE_INSTALL_FULL_INCLUDEDIR@/phasar"
+ "@CMAKE_INSTALL_FULL_LIBDIR@/cmake/phasar"
+ "@CMAKE_INSTALL_FULL_LIBDIR@/phasar"
+)
+
+set(PHASAR_CONFIG_INSTALL_DIR "@PHASAR_CONFIG_INSTALL_DIR@")
+if (IS_ABSOLUTE "${PHASAR_CONFIG_INSTALL_DIR}")
+ list(APPEND PHASAR_OWNED_INSTALL_DIRECTORIES "${PHASAR_CONFIG_INSTALL_DIR}")
+else()
+ list(APPEND PHASAR_OWNED_INSTALL_DIRECTORIES "@CMAKE_INSTALL_PREFIX@/${PHASAR_CONFIG_INSTALL_DIR}")
+endif()
+
+foreach (PHASAR_OWNED_INSTALL_DIRECTORY IN LISTS PHASAR_OWNED_INSTALL_DIRECTORIES)
+ phasar_remove_empty_directories("$ENV{DESTDIR}${PHASAR_OWNED_INSTALL_DIRECTORY}")
+endforeach()
diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake
index 961d57697d..cae0ed9691 100644
--- a/cmake/phasar_macros.cmake
+++ b/cmake/phasar_macros.cmake
@@ -70,9 +70,7 @@ function(generate_ll_file)
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()
+ cmake_path(NORMAL_PATH hint OUTPUT_VARIABLE hint)
list(APPEND binary_hint_paths "${hint}")
endforeach()
message(STATUS "HINTS to find clang/clang++/opt: ${binary_hint_paths}")
@@ -277,10 +275,21 @@ function(add_phasar_library name)
add_library(phasar::${component_name} ALIAS ${name})
set_target_properties(${name} PROPERTIES
EXPORT_NAME ${component_name}
+ VERSION ${PHASAR_VERSION}
+ SOVERSION ${PHASAR_VERSION}
)
target_compile_features(${name} PUBLIC cxx_std_20)
+ if (BUILD_SHARED_LIBS AND UNIX)
+ target_link_options(${name} INTERFACE "LINKER:-rpath,\$ORIGIN/../${CMAKE_INSTALL_LIBDIR}:\$ORIGIN/../${PHASAR_DEPS_INSTALL_DESTINATION}/lib")
+
+ set_target_properties(${name} PROPERTIES
+ INSTALL_RPATH
+ "\$ORIGIN/../${CMAKE_INSTALL_LIBDIR};\$ORIGIN/../${PHASAR_DEPS_INSTALL_DESTINATION}/lib"
+ )
+ endif()
+
set(install_module)
if(PHASAR_LIB_MODULE_FILES)
if(PHASAR_BUILD_MODULES)
diff --git a/config.h.in b/config.h.in
index 926d69b0bf..b8d5423b9a 100644
--- a/config.h.in
+++ b/config.h.in
@@ -7,6 +7,7 @@
#cmakedefine PAMM_CORE
#cmakedefine PAMM_FULL
+#cmakedefine PHASAR_THREAD_SAFE_PAMM
#cmakedefine DYNAMIC_LOG
#cmakedefine BUILD_PHASAR_CLANG
diff --git a/config/double-free-config.json b/config/double-free-config.json
index 043b392288..e5595cc093 100644
--- a/config/double-free-config.json
+++ b/config/double-free-config.json
@@ -23,6 +23,17 @@
0
]
}
+ },
+ {
+ "name": "_ZdaPv",
+ "params": {
+ "source": [
+ 0
+ ],
+ "sink": [
+ 0
+ ]
+ }
}
]
}
diff --git a/docs/README.dox b/docs/README.dox
index 6ae3b786c0..eb2643421b 100644
--- a/docs/README.dox
+++ b/docs/README.dox
@@ -16,7 +16,7 @@ PhASAR is primarily developed and maintained by the [Secure Software Engineering
PhASAR was initially developed by Philipp Dominik Schubert (@pdschubert)().
\b Currently, PhASAR is maintained by
-- Fabian Schiebel (@fabianbs96)(fabian.schiebel@iem.fraunhofer.de)
+- Fabian Schiebel (@fabianbs96)(fabian.schiebel@uni-paderborn.de)
- Sriteja Kummita (@sritejakv)
- Lucas Briese (@jusito)
- Martin Mory (@MMory)(martin.mory@upb.de)
diff --git a/examples/how-to/00-load-llvm-ir/CMakeLists.txt b/examples/how-to/00-load-llvm-ir/CMakeLists.txt
index 9d572feff4..9da3739f9d 100644
--- a/examples/how-to/00-load-llvm-ir/CMakeLists.txt
+++ b/examples/how-to/00-load-llvm-ir/CMakeLists.txt
@@ -11,8 +11,8 @@ target_link_libraries(load-llvm-ir PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_load_llvm_ir
- DEPENDS load-llvm-ir
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/simple.ll"
+ DEPENDS load-llvm-ir LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/simple_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_load_llvm_ir)
diff --git a/examples/how-to/01-build-type-hierarchy/CMakeLists.txt b/examples/how-to/01-build-type-hierarchy/CMakeLists.txt
index f6e0303099..d545f59128 100644
--- a/examples/how-to/01-build-type-hierarchy/CMakeLists.txt
+++ b/examples/how-to/01-build-type-hierarchy/CMakeLists.txt
@@ -11,8 +11,8 @@ target_link_libraries(build-type-hierarchy PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_build_type_hierarchy
- DEPENDS build-type-hierarchy
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll"
+ DEPENDS build-type-hierarchy LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/class_hierarchy_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_build_type_hierarchy)
diff --git a/examples/how-to/02-build-call-graph/CMakeLists.txt b/examples/how-to/02-build-call-graph/CMakeLists.txt
index 56d5fc25d8..b0b8c67289 100644
--- a/examples/how-to/02-build-call-graph/CMakeLists.txt
+++ b/examples/how-to/02-build-call-graph/CMakeLists.txt
@@ -14,9 +14,9 @@ target_link_libraries(build-llvm-based-call-graph PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_build_call_graph
- DEPENDS build-llvm-based-icfg build-llvm-based-call-graph
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/class_hierarchy.ll"
+ DEPENDS build-llvm-based-icfg build-llvm-based-call-graph LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/class_hierarchy_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/class_hierarchy_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_build_call_graph)
diff --git a/examples/how-to/03-create-alias-info/CMakeLists.txt b/examples/how-to/03-create-alias-info/CMakeLists.txt
index d47aa2d3a2..634de638dd 100644
--- a/examples/how-to/03-create-alias-info/CMakeLists.txt
+++ b/examples/how-to/03-create-alias-info/CMakeLists.txt
@@ -9,10 +9,14 @@ find_package(phasar REQUIRED CONFIG)
add_executable(create-alias-info main.cpp)
target_link_libraries(create-alias-info PRIVATE phasar::phasar)
+add_executable(create-alias-info-andersen andersen.cpp)
+target_link_libraries(create-alias-info-andersen PRIVATE phasar::phasar)
+
if (TARGET run_sample_programs)
add_custom_target(run_create_alias_info
- DEPENDS create-alias-info
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/pointers.ll"
+ DEPENDS create-alias-info create-alias-info-andersen LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/pointers_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/pointers_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_create_alias_info)
diff --git a/examples/how-to/03-create-alias-info/andersen.cpp b/examples/how-to/03-create-alias-info/andersen.cpp
new file mode 100644
index 0000000000..266c9b4047
--- /dev/null
+++ b/examples/how-to/03-create-alias-info/andersen.cpp
@@ -0,0 +1,62 @@
+#include "phasar/PhasarLLVM/ControlFlow.h"
+#include "phasar/PhasarLLVM/DB.h"
+#include "phasar/PhasarLLVM/Pointer.h"
+#include "phasar/PhasarLLVM/Utils.h"
+
+#include "llvm/IR/InstIterator.h"
+
+#include
+
+int main(int Argc, char *Argv[]) {
+ using namespace std::string_literals;
+ if (Argc < 2) {
+ llvm::errs() << "USAGE: create-alias-info-andersen \n";
+ return 1;
+ }
+
+ // Load the IR
+ psr::LLVMProjectIRDB IRDB(Argv[1]);
+ if (!IRDB) {
+ return 1;
+ }
+
+ // Mapping the entry-points (here, just the main function) to LLVM IR:
+ auto Entrypoints = psr::getEntryFunctions(IRDB, {"main"s});
+
+ // Computing the Andersen-stale alias information.
+ auto Aliases = psr::computeAndersenOTF(IRDB, Entrypoints);
+
+ // The Andersen alias result is compatible with the LLVMAliasIteratorRef
+ // interface.
+ psr::LLVMAliasIteratorRef AIt = &Aliases;
+
+ const auto *MainF = IRDB.getFunctionDefinition("main");
+ if (!MainF) {
+ llvm::errs() << "Required function 'main' not found\n";
+ return 1;
+ }
+
+ // Manually printing the alias sets:
+
+ for (const auto &Inst : llvm::instructions(MainF)) {
+ if (!Inst.getType()->isPointerTy()) {
+ // For aliasing, we only care about pointers...
+ continue;
+ }
+
+ llvm::outs() << "For pointer " << psr::llvmIRToString(&Inst) << ":\n";
+
+ // Iterate over the aliases of the result of the instruction Inst (first
+ // parameter) at the program location determined by Inst (second parameter).
+ //
+ // Implementations may ignore the second parameter.
+ Aliases.forallAliasesOf(&Inst, &Inst, [&](const llvm::Value *Alias) {
+ llvm::outs() << "> aliasing " << psr::llvmIRToShortString(Alias) << '\n';
+
+ // You can also check, whether two pointers are (potentially) aliasing:
+ assert(Aliases.mayAlias(&Inst, Alias, &Inst));
+ });
+
+ llvm::outs() << '\n';
+ }
+}
diff --git a/examples/how-to/03-create-alias-info/main.cpp b/examples/how-to/03-create-alias-info/main.cpp
index b45e969418..a40f10a77b 100644
--- a/examples/how-to/03-create-alias-info/main.cpp
+++ b/examples/how-to/03-create-alias-info/main.cpp
@@ -30,6 +30,13 @@ int main(int Argc, char *Argv[]) {
// it.
psr::LLVMAliasInfoRef ASRef = &AS;
+ // For APIs that don't need the expressiveness of the LLVMAliasInfoRef, PhASAR
+ // provides a simpler interface: LLVMAliasIteratorRef. It only provides a
+ // function forallAliasesOf() that allows invoking a callback for all aliases
+ // of a pointer; for most applications, this is sufficient.
+ // Similar to LLVMAliasInfoRef, it is a non-owning reference.
+ psr::LLVMAliasIteratorRef AIt = &AS;
+
// You can print and load alias information from/to JSON:
AS.printAsJson();
diff --git a/examples/how-to/04-run-ifds-analysis/CMakeLists.txt b/examples/how-to/04-run-ifds-analysis/CMakeLists.txt
index 6b8c99365b..3e15f364a7 100644
--- a/examples/how-to/04-run-ifds-analysis/CMakeLists.txt
+++ b/examples/how-to/04-run-ifds-analysis/CMakeLists.txt
@@ -20,11 +20,11 @@ target_link_libraries(run-ifds-analysis-ifds-solver PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_run_ifds_analysis
- DEPENDS run-ifds-analysis-simple run-ifds-analysis-helper-analyses run-ifds-analysis-otf-reporter run-ifds-analysis-ifds-solver
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
+ DEPENDS run-ifds-analysis-simple run-ifds-analysis-helper-analyses run-ifds-analysis-otf-reporter run-ifds-analysis-ifds-solver LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_run_ifds_analysis)
diff --git a/examples/how-to/05-run-ide-analysis/CMakeLists.txt b/examples/how-to/05-run-ide-analysis/CMakeLists.txt
index cc71bd81a8..f164b25ac6 100644
--- a/examples/how-to/05-run-ide-analysis/CMakeLists.txt
+++ b/examples/how-to/05-run-ide-analysis/CMakeLists.txt
@@ -17,10 +17,10 @@ target_link_libraries(run-ide-analysis-ide-solver PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_run_ide_analysis
- DEPENDS run-ide-analysis-simple run-ide-analysis-helper-analyses run-ide-analysis-ide-solver
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll"
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/call2.ll"
+ DEPENDS run-ide-analysis-simple run-ide-analysis-helper-analyses run-ide-analysis-ide-solver LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/call2_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/call2_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/call2_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_run_ide_analysis)
diff --git a/examples/how-to/06-run-monoifds-analysis/CMakeLists.txt b/examples/how-to/06-run-monoifds-analysis/CMakeLists.txt
new file mode 100644
index 0000000000..87651cc8b3
--- /dev/null
+++ b/examples/how-to/06-run-monoifds-analysis/CMakeLists.txt
@@ -0,0 +1,25 @@
+cmake_minimum_required(VERSION 3.14...3.28)
+
+project(run-monoifds-analysis)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+find_package(phasar REQUIRED CONFIG)
+
+
+add_executable(run-monoifds-analysis-helper-analyses helper-analyses.cpp)
+target_link_libraries(run-monoifds-analysis-helper-analyses PRIVATE phasar::phasar)
+
+add_executable(run-monoifds-analysis-manual manual.cpp)
+target_link_libraries(run-monoifds-analysis-manual PRIVATE phasar::phasar)
+
+
+if (TARGET run_sample_programs)
+ add_custom_target(run_run_monoifds_analysis
+ DEPENDS run-monoifds-analysis-helper-analyses run-monoifds-analysis-manual LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
+ )
+
+ add_dependencies(run_sample_programs run_run_monoifds_analysis)
+endif()
diff --git a/examples/how-to/06-run-monoifds-analysis/README.md b/examples/how-to/06-run-monoifds-analysis/README.md
new file mode 100644
index 0000000000..1fb5ec1a32
--- /dev/null
+++ b/examples/how-to/06-run-monoifds-analysis/README.md
@@ -0,0 +1,30 @@
+# Run a MonoIFDS Analysis
+
+Shows some way, how you can use PhASAR to run an already existing MonoIFDS analysis on a LLVM IR module.
+For this example, we selected the `MonoIFDSTaintAnalysis`.
+
+You may look at the different C++ source files to see, how you can run an MonoIFDS taint analysis using PhASAR.
+We suggest to start with the simplest example [helper-analyses.cpp](./helper-analyses.cpp).
+
+## Build
+
+This example program can be built using cmake.
+It assumes, that you have installed PhASAR on your system. If you did not install PhASAR to a default location, you can specify `-Dphasar_ROOT=your/path/to/phasar` when invoking `cmake`, replacing "your/path/to/phasar" by the actual path where you have installed PhASAR.
+
+```bash
+# Invoked from the 06-run-monoifds-analysis root folder:
+$ mkdir -p build && cd build
+$ cmake ..
+$ cmake --build .
+```
+
+## Test
+
+You can test the example program on the target programs from [llvm-hello-world/target](../../llvm-hello-world/target/).
+
+```bash
+# Invoked from the 06-run-monoifds-analysis/build folder:
+./run-monoifds-analysis-helper-analyses ../../../llvm-hello-world/target/taint.ll
+
+./run-monoifds-analysis-manual ../../../llvm-hello-world/target/taint.ll
+```
diff --git a/examples/how-to/06-run-monoifds-analysis/helper-analyses.cpp b/examples/how-to/06-run-monoifds-analysis/helper-analyses.cpp
new file mode 100644
index 0000000000..2449037199
--- /dev/null
+++ b/examples/how-to/06-run-monoifds-analysis/helper-analyses.cpp
@@ -0,0 +1,51 @@
+#include "phasar/DataFlow.h" // For MonoIFDSSolver
+#include "phasar/PhasarLLVM.h" // For the HelperAnalyses
+#include "phasar/PhasarLLVM/DataFlow.h" // For the MonoIFDSTaintAnalysis
+#include "phasar/PhasarLLVM/Pointer/FilteredLLVMAliasIterator.h"
+#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig
+
+int main(int Argc, char *Argv[]) {
+ if (Argc < 2) {
+ llvm::errs()
+ << "USAGE: run-monoifds-analysis-helper-analyses \n";
+ return 1;
+ }
+
+ using namespace std::string_literals;
+ std::vector EntryPoints = {"main"s};
+
+ // Instead of creating all the helper analyses ourselves, we can just use the
+ // HelperAnalyses class. It will create the necessary information on-demand.
+ //
+ // You can customize the underlying algorithms by passing a
+ // HelperAnalysisConfig as third parameter
+ psr::HelperAnalyses HA(Argv[1], EntryPoints);
+ if (!HA.getProjectIRDB()) {
+ return 1;
+ }
+
+ // Create the taint configuration
+ psr::LLVMTaintConfig TC(HA.getProjectIRDB());
+ TC.print();
+ llvm::outs() << "------------------------\n";
+
+ // More precise alias-information; techically, this is not required, but it
+ // helps a lot
+ psr::FilteredLLVMAliasIterator FAI(HA.getAliasInfo());
+
+ // Create the taint analysis problem:
+ psr::monoifds::TaintAnalysis TaintProblem(&TC, &HA.getUsedGlobals(), &FAI);
+
+ // To solve the taint problem, we now create an instance of the
+ // MonoIFDSSolver. Passing the HelperAnalyses here, lets the solver
+ // automatically grab the needed information
+ psr::monoifds::MonoIFDSSolver Solver(&TaintProblem, HA);
+
+ // Solves the taint problem. This may take some time.
+ Solver.solve();
+
+ // The monoifds::TaintAnalysis is set-up to use the analysis-printer (see
+ // ../04-run-ifds-analysis/otf-reporter.cpp). By default, it prints the
+ // detected leaks into the given llvm::raw_ostream
+ TaintProblem.emitTextReport(llvm::outs());
+}
diff --git a/examples/how-to/06-run-monoifds-analysis/manual.cpp b/examples/how-to/06-run-monoifds-analysis/manual.cpp
new file mode 100644
index 0000000000..e6986f7053
--- /dev/null
+++ b/examples/how-to/06-run-monoifds-analysis/manual.cpp
@@ -0,0 +1,76 @@
+#include "phasar/DataFlow.h" // For MonoIFDSSolver
+#include "phasar/PhasarLLVM/ControlFlow.h" // For FunctionCompressor & getEntryFunctions
+#include "phasar/PhasarLLVM/DataFlow.h" // For the MonoIFDSTaintAnalysis
+#include "phasar/PhasarLLVM/Pointer.h" // For the LLVMAliasSet
+#include "phasar/PhasarLLVM/TaintConfig.h" // For the LLVMTaintConfig
+#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h"
+#include "phasar/PhasarLLVM/Utils/UsedGlobals.h"
+
+int main(int Argc, char *Argv[]) {
+ if (Argc < 2) {
+ llvm::errs() << "USAGE: run-monoifds-analysis-manual \n";
+ return 1;
+ }
+
+ using namespace std::string_literals;
+ std::vector EntryPoints = {"main"s};
+
+ // Load the IR
+ auto IRDB = psr::LLVMProjectIRDB::loadOrExit(Argv[1]);
+
+ // The MonoIFDSTaintAnalysis requires alias information, so create it here
+ psr::LLVMAliasSet AS(&IRDB);
+
+ // We use a type-hierarchy to build the call-graph (LLVMBasedICFG below)
+ psr::DIBasedTypeHierarchy TH(IRDB);
+
+ // Create the ICFG
+ psr::LLVMBasedICFG ICFG(&IRDB, psr::CallGraphAnalysisType::VTA, {"main"}, &TH,
+ &AS);
+
+ // Assign each reachable llvm::Function in the call-graph a sequential ID.
+ // This is needed for SCC computation and for the solver
+ auto Funs = psr::compressFunctions(ICFG.getCallGraph(),
+ psr::getEntryFunctions(IRDB, EntryPoints));
+
+ // Compute the call-graph SCCs.
+ auto CGSCCs = computeCGSCCs(ICFG, Funs);
+
+ // Build a dependency-graph induced by the call-graph, collapsing each SCC to
+ // a single node
+ auto SCCC = computeCGSCCCallers(ICFG, Funs, CGSCCs);
+
+ // For each CGSCC, compute which global variables are (transitively) used by
+ // any function in that SCC
+ auto UG = psr::computeUsedGlobals(IRDB, Funs, CGSCCs, SCCC);
+
+ // Create the taint configuration
+ psr::LLVMTaintConfig TC(IRDB);
+ TC.print();
+ llvm::outs() << "------------------------\n";
+
+ // More precise alias-information; techically, this is not required, but it
+ // helps a lot
+ psr::FilteredLLVMAliasIterator FAI(&AS);
+
+ // Create the taint analysis problem:
+ psr::monoifds::TaintAnalysis TaintProblem(&TC, &UG, &FAI);
+
+ // To solve the taint problem, we now create an instance of the
+ // MonoIFDSSolver. Passing the HelperAnalyses here, lets the solver
+ // automatically grab the needed information
+ psr::monoifds::MonoIFDSSolver Solver(&TaintProblem, &ICFG);
+
+ // Supply the solver with the previously computed helper information. If we
+ // don't provide this, the solver would compute them on its own once solve()
+ // is called.
+ Solver.setCGSCCs(&CGSCCs).setFunctionCompressor(&Funs);
+
+ // Solves the taint problem. This may take some time.
+ Solver.solve();
+
+ // The monoifds::TaintAnalysis is set-up to use the analysis-printer (see
+ // ../04-run-ifds-analysis/otf-reporter.cpp). By default, it prints the
+ // detected leaks into the given llvm::raw_ostream
+ TaintProblem.emitTextReport(llvm::outs());
+}
diff --git a/examples/how-to/07-write-ifds-analysis/CMakeLists.txt b/examples/how-to/07-write-ifds-analysis/CMakeLists.txt
index fe1839e072..73d12d8bc3 100644
--- a/examples/how-to/07-write-ifds-analysis/CMakeLists.txt
+++ b/examples/how-to/07-write-ifds-analysis/CMakeLists.txt
@@ -12,8 +12,8 @@ target_link_libraries(write-ifds-analysis-simple PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_write_ifds_analysis
- DEPENDS write-ifds-analysis-simple
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
+ DEPENDS write-ifds-analysis-simple LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_write_ifds_analysis)
diff --git a/examples/how-to/07-write-ifds-analysis/simple.cpp b/examples/how-to/07-write-ifds-analysis/simple.cpp
index 34ea367b47..b6b07a1b4a 100644
--- a/examples/how-to/07-write-ifds-analysis/simple.cpp
+++ b/examples/how-to/07-write-ifds-analysis/simple.cpp
@@ -34,13 +34,14 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem {
/// Provides the initial seeds, i.e., the pairs that are assumed
/// to hold un-conditionally at the beginning of the analysis.
/// This is the start state that the IFDS solver will use to start with.
- [[nodiscard]] psr::InitialSeeds initialSeeds() override {
+ [[nodiscard]] psr::InitialSeeds initialSeeds() {
psr::InitialSeeds Seeds;
psr::LLVMBasedCFG CFG;
// Here, we just say that for all entry-functions in the EntryPoints, the
// zero-value should hold at the very first statement.
- addSeedsForStartingPoints(EntryPoints, IRDB, CFG, Seeds, getZeroValue());
+ addSeedsForStartingPoints(EntryPoints, getProjectIRDB(), CFG, Seeds,
+ getZeroValue());
return Seeds;
};
@@ -48,8 +49,8 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem {
/// Here, we define special semantics of function-calls that are specified
/// outside of the target program. In the case of taint analysis, we need to
/// handle sources, sinks and sanitizers here:
- [[nodiscard]] FlowFunctionPtrType
- getSummaryFlowFunction(n_t CallSite, f_t DestFun) override {
+ [[nodiscard]] FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite,
+ f_t DestFun) {
const auto *CS = llvm::cast(CallSite);
// Process the effects of source or sink functions that are called
@@ -60,7 +61,7 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem {
if (Gen.empty() && Leak.empty() && Kill.empty()) {
// This CallSite apparently is not calling a special source/sink/sanitizer
// function. Fallback to the default-behavior.
- return DefaultAliasAwareIFDSProblem::getSummaryFlowFunction(CS, DestFun);
+ return nullptr;
}
// Since our analysis is alias-aware, we must handle aliasing here:
@@ -76,13 +77,13 @@ class ExampleTaintAnalysis : public psr::DefaultAliasAwareIFDSProblem {
return Gen;
}
- if (Leak.count(Source)) {
+ if (Leak.contains(Source)) {
// In case of a sink, we create a leak if one of the leaking parameters
// (Leak) is tainted (Source).
Leaks.insert(CS);
}
- if (Kill.count(Source)) {
+ if (Kill.contains(Source)) {
// In case of a sanitizer, we kill tainted values (Source) that flow
// into the sanitizied parameters (Kill).
return {};
diff --git a/examples/how-to/08-write-ide-analysis/CMakeLists.txt b/examples/how-to/08-write-ide-analysis/CMakeLists.txt
index 25d4088d49..2cc2ec736e 100644
--- a/examples/how-to/08-write-ide-analysis/CMakeLists.txt
+++ b/examples/how-to/08-write-ide-analysis/CMakeLists.txt
@@ -12,8 +12,8 @@ target_link_libraries(write-ide-analysis-simple PRIVATE phasar::phasar)
if (TARGET run_sample_programs)
add_custom_target(run_write_ide_analysis
- DEPENDS write-ide-analysis-simple
- COMMAND $ "${CMAKE_CURRENT_LIST_DIR}/../../llvm-hello-world/target/taint.ll"
+ DEPENDS write-ide-analysis-simple LLFileGeneration
+ COMMAND $ "${CMAKE_CURRENT_BINARY_DIR}/../llvm-hello-world/target/taint_cpp_dbg.ll"
)
add_dependencies(run_sample_programs run_write_ide_analysis)
diff --git a/examples/how-to/08-write-ide-analysis/simple.cpp b/examples/how-to/08-write-ide-analysis/simple.cpp
index 45443ced08..d8d5ce7444 100644
--- a/examples/how-to/08-write-ide-analysis/simple.cpp
+++ b/examples/how-to/08-write-ide-analysis/simple.cpp
@@ -54,19 +54,19 @@ class ExampleLinearConstantAnalysis
/// to hold un-conditionally at the beginning of the analysis.
/// Similar to IFDS, this is the start state that the IDE solver will use to
/// start with.
- [[nodiscard]] psr::InitialSeeds initialSeeds() override {
+ [[nodiscard]] psr::InitialSeeds initialSeeds() {
psr::InitialSeeds Seeds;
psr::LLVMBasedCFG CFG;
// Here, we just say that for all entry-functions in the EntryPoints, the
// zero-value should hold at the very first statement.
- addSeedsForStartingPoints(EntryPoints, IRDB, CFG, Seeds, getZeroValue(),
- bottomElement());
+ addSeedsForStartingPoints(EntryPoints, getProjectIRDB(), CFG, Seeds,
+ getZeroValue(), bottomElement());
return Seeds;
};
- FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override {
+ FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) {
if (const auto *Alloca = llvm::dyn_cast(Curr)) {
// Freshly allocated variables hold no constant value
@@ -88,7 +88,7 @@ class ExampleLinearConstantAnalysis
return this->DefaultNoAliasIDEProblem::getNormalFlowFunction(Curr, Succ);
}
- FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) override {
+ FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) {
// We definitely want to re-use as much as possible from the default
// call-flow-function
auto DefaultFn =
@@ -118,7 +118,7 @@ class ExampleLinearConstantAnalysis
}
FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun,
- n_t ExitInst, n_t RetSite) override {
+ n_t ExitInst, n_t RetSite) {
auto DefaultFn = this->DefaultNoAliasIDEProblem::getRetFlowFunction(
CallSite, CalleeFun, ExitInst, RetSite);
@@ -352,8 +352,7 @@ class ExampleLinearConstantAnalysis
};
psr::EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode,
- n_t /*Succ*/,
- d_t SuccNode) override {
+ n_t /*Succ*/, d_t SuccNode) {
if (isZeroValue(CurrNode) && !isZeroValue(SuccNode)) {
// Handle the two cases, where we generate facts from zero:
@@ -384,8 +383,11 @@ class ExampleLinearConstantAnalysis
}
// Attach the arithmetic transformer to this edge
- return BinOp{Op, llvm::dyn_cast(Lop),
- llvm::dyn_cast(Rop)};
+ return BinOp{
+ .OpCode = Op,
+ .LeftConst = llvm::dyn_cast(Lop),
+ .RightConst = llvm::dyn_cast(Rop),
+ };
}
// Pass everything else as identity
@@ -394,7 +396,7 @@ class ExampleLinearConstantAnalysis
psr::EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode,
f_t /*DestinationFunction*/,
- d_t DestNode) override {
+ d_t DestNode) {
if (isZeroValue(SrcNode) && !isZeroValue(DestNode)) {
// If a constant int is passed as parameter, we need to generate the
// parameter inside the callee from zero
@@ -408,9 +410,10 @@ class ExampleLinearConstantAnalysis
return psr::EdgeIdentity{};
}
- psr::EdgeFunction
- getReturnEdgeFunction(n_t CallSite, f_t /*CalleeFunction*/, n_t ExitStmt,
- d_t ExitNode, n_t /*RetSite*/, d_t RetNode) override {
+ psr::EdgeFunction getReturnEdgeFunction(n_t CallSite,
+ f_t /*CalleeFunction*/,
+ n_t ExitStmt, d_t ExitNode,
+ n_t /*RetSite*/, d_t RetNode) {
if (isZeroValue(ExitNode) && RetNode == CallSite) {
// If we return a literal constant int, we must generate the corresponding
// value at the call-site from zero, i.e., the CallSite itself in case of
@@ -428,7 +431,7 @@ class ExampleLinearConstantAnalysis
psr::EdgeFunction
getCallToRetEdgeFunction(n_t /*CallSite*/, d_t /*CallNode*/, n_t /*RetSite*/,
d_t /*RetSiteNode*/,
- llvm::ArrayRef /*Callees*/) override {
+ llvm::ArrayRef /*Callees*/) {
// The call-to-return edge-function handles facts that are not affected by
// the call. This is usually the identity function.
return psr::EdgeIdentity{};
diff --git a/examples/how-to/09-export-callsite-cg/CMakeLists.txt b/examples/how-to/09-export-callsite-cg/CMakeLists.txt
new file mode 100644
index 0000000000..2a55249f35
--- /dev/null
+++ b/examples/how-to/09-export-callsite-cg/CMakeLists.txt
@@ -0,0 +1,10 @@
+cmake_minimum_required(VERSION 3.14...3.28)
+
+project(export-callsite-cg)
+
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+find_package(phasar REQUIRED CONFIG)
+
+add_executable(export-callsite-cg-streaming export_callsite_cg_streaming.cpp)
+target_link_libraries(export-callsite-cg-streaming PRIVATE phasar::phasar)
diff --git a/examples/how-to/09-export-callsite-cg/README.md b/examples/how-to/09-export-callsite-cg/README.md
new file mode 100644
index 0000000000..1eafec8047
--- /dev/null
+++ b/examples/how-to/09-export-callsite-cg/README.md
@@ -0,0 +1,40 @@
+# Export a Callsite-Level Call Graph
+
+Exports PhASAR's call graph as callsite-resolved, source-located CSV: one row per
+resolved edge, with caller/callee function name plus file/line/column for each
+side, written incrementally as edges are discovered.
+
+This differs from `CallGraph::printAsDot()` / `printAsJson()` in two ways:
+
+- **Callsite-level.** Each row is a single call instruction resolved to its callee(s), rather than an aggregated function-to-function edge.
+ This is what, e.g., correlating a static call graph against runtime instrumentation data needs.
+ The runtime side reports individual call sites, not just "A calls B somewhere".
+- **Streaming output.** `exportICFGAsJson()` and `printAsJson()` build one in-memory `nlohmann::json` object holding every edge before writing anything to disk.
+ On a large enough input (validated against a ~1.2M-edge FFmpeg call graph) this easily goes out-of-memory.
+ This driver writes each edge to disk immediately and discards it, so memory stays roughly constant regardless of total edge count.
+
+It also documents a (current) source-location bug it works around: PhASAR's `getSrcCodeInfoFromIR` resolves `File` and `Line` through separate helper calls,
+each with its own fallback logic for instructions lacking direct `!dbg` metadata, which can pair a `File` from one resolution path with a `Line` from an unrelated one.
+
+## Build
+
+```bash
+# Invoked from the 09-export-callsite-cg root folder:
+$ mkdir -p build && cd build
+$ cmake ..
+$ cmake --build .
+```
+
+## Usage
+
+```bash
+./export-callsite-cg-streaming [cha|rta|vta|otf]
+```
+
+Output is CSV with one row per resolved call edge:
+
+```
+caller_function,caller_file,caller_line,caller_column,callee_function,callee_file,callee_line,callee_column,caller_loc_approximate
+```
+
+`caller_loc_approximate` is `true` when the call instruction had no direct debug location and the row fell back to the enclosing function's declaration site instead of the real call site.
diff --git a/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp b/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp
new file mode 100644
index 0000000000..c51d6659c1
--- /dev/null
+++ b/examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp
@@ -0,0 +1,152 @@
+#include "phasar/ControlFlow/CallGraphAnalysisType.h"
+#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h"
+#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h"
+#include "phasar/PhasarLLVM/HelperAnalyses.h"
+#include "phasar/PhasarLLVM/HelperAnalysisConfig.h"
+#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h"
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/DebugInfoMetadata.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Instructions.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include
+
+namespace {
+struct ResolvedLoc {
+ uint32_t Line{};
+ uint32_t Column{};
+ llvm::StringRef FileName{};
+ bool Approximate{}; // true = fell back to enclosing function's declaration
+ // site, not the real call site
+
+ friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
+ const ResolvedLoc &Loc) {
+ OS.write_escaped(Loc.FileName) << ',';
+ return OS << Loc.Line << ',' << Loc.Column;
+ }
+};
+
+constexpr llvm::StringLiteral NoDebugInfo = "";
+
+ResolvedLoc resolveLocation(const llvm::Instruction *I) {
+ if (auto Loc = psr::getDebugLocation(I)) {
+ return {
+ .Line = Loc->Line,
+ .Column = Loc->Column,
+ .FileName = Loc->File->getFilename(),
+ .Approximate = false,
+ };
+ }
+
+ // No direct location on this instruction -- fall back to the
+ // enclosing function's declared file
+ if (const auto *SP = I->getFunction()->getSubprogram()) {
+ return {
+ .Line = SP->getLine(),
+ .Column = 0,
+ .FileName = SP->getFile()->getFilename(),
+ .Approximate = true,
+ };
+ }
+ return {.FileName = NoDebugInfo, .Approximate = true};
+}
+
+ResolvedLoc resolveLocation(const llvm::Function *F) {
+ if (const auto *SP = F->getSubprogram()) {
+ return {
+ .Line = SP->getLine(),
+ .Column = 0,
+ .FileName = SP->getFile()->getFilename(),
+ .Approximate = false,
+ };
+ }
+ return {.FileName = NoDebugInfo, .Approximate = true};
+}
+} // namespace
+
+int main(int Argc, char **Argv) {
+ if (Argc < 5) {
+ llvm::errs() << "USAGE: " << Argv[0]
+ << " [cha|rta|vta|otf] \n";
+ return 1;
+ }
+
+ llvm::StringRef IRFile = Argv[1];
+ std::vector EntryPoints = {Argv[2]};
+ auto CGTy = psr::toCallGraphAnalysisType(Argv[3]);
+ llvm::StringRef OutFile = Argv[4];
+
+ if (CGTy == psr::CallGraphAnalysisType::Invalid) {
+ llvm::WithColor::error() << "Invalid call-graph type '" << Argv[3] << "'\n";
+ return 1;
+ }
+
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(OutFile, EC);
+ if (EC) {
+ llvm::WithColor::error()
+ << "While opening output file '" << OutFile << "':\n";
+ llvm::WithColor::error() << EC.message() << '\n';
+ return 1;
+ }
+
+ // CSV header:
+ OS << "caller_function,caller_file,caller_line,caller_column,"
+ "callee_function,callee_file,callee_line,callee_column,"
+ "caller_loc_approximate\n";
+ OS.flush();
+
+ // Basic phasar pipeline:
+ psr::HelperAnalyses HA{
+ std::make_unique(
+ psr::LLVMProjectIRDB::loadOrExit(IRFile)),
+ std::move(EntryPoints),
+ psr::HelperAnalysisConfig{.CGTy = CGTy},
+ };
+
+ auto &ICF = HA.getICFG();
+
+ size_t EdgeCount = 0;
+ size_t FnCount = 0;
+
+ // Walk every function PhASAR's call-graph knows about, and ask it for all
+ // known callers, writing (and forgetting) each result immediately instead of
+ // accumulating all of them.
+ ICF.getCallGraph().iterateCallersView([&](const llvm::Function *Fun,
+ const auto &Callers) {
+ auto FunName = Fun->getName();
+ ++FnCount;
+
+ ResolvedLoc CalleeLoc = resolveLocation(Fun);
+ for (const auto *CS : Callers) {
+ ResolvedLoc CallerLoc = resolveLocation(CS);
+
+ OS.write_escaped(CS->getFunction()->getName()) << ',' << CallerLoc << ',';
+ OS.write_escaped(FunName) << ',' << CalleeLoc << ',';
+ OS << (CallerLoc.Approximate ? "true" : "false") << '\n';
+
+ ++EdgeCount;
+ if (EdgeCount % 50000 == 0) {
+ OS.flush(); // periodic flush -- makes progress visible on
+ // disk instead of buffered invisibly
+ llvm::WithColor::note()
+ << "[export_callsite_cg_streaming] " << EdgeCount
+ << " edges written, " << FnCount << '/'
+ << ICF.getNumVertexFunctions() << " functions processed so far\n";
+ }
+ }
+ });
+
+ OS.close();
+
+ llvm::WithColor::note() << "[export_callsite_cg_streaming] DONE: "
+ << EdgeCount << " total edges across " << FnCount
+ << " functions -> "
+ << (OutFile == "-" ? llvm::StringRef("stdout")
+ : OutFile)
+ << "\n";
+ return 0;
+}
diff --git a/examples/how-to/CMakeLists.txt b/examples/how-to/CMakeLists.txt
index 5a1ae5b094..134129ab49 100644
--- a/examples/how-to/CMakeLists.txt
+++ b/examples/how-to/CMakeLists.txt
@@ -11,3 +11,8 @@ foreach(child ${children})
add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/${child})
endif()
endforeach()
+
+find_package(phasar REQUIRED CONFIG)
+
+message("PHASAR_LLVM_VERSION LLVM-${PHASAR_LLVM_VERSION}")
+add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/../llvm-hello-world ${CMAKE_CURRENT_BINARY_DIR}/llvm-hello-world)
diff --git a/examples/how-to/README.md b/examples/how-to/README.md
index b84875a609..10c601fc45 100644
--- a/examples/how-to/README.md
+++ b/examples/how-to/README.md
@@ -11,3 +11,4 @@ Currently supporting:
- [x] Run an IDE analysis ([here](./05-run-ide-analysis/README.md))
- [x] Write an IFDS analysis ([here](./07-write-ifds-analysis/README.md))
- [x] Write an IDE analysis ([here](./08-write-ide-analysis/))
+- [x] Export a callsite-level call graph ([here](./09-export-callsite-cg/README.md))
diff --git a/examples/llvm-hello-world/CMakeLists.txt b/examples/llvm-hello-world/CMakeLists.txt
index 8ed9e80124..366009c806 100644
--- a/examples/llvm-hello-world/CMakeLists.txt
+++ b/examples/llvm-hello-world/CMakeLists.txt
@@ -4,7 +4,14 @@ project(llvm-hello-world)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
-find_package(LLVM 15 REQUIRED CONFIG)
+set(LLVM_HW_LLVM_VERSION 15)
+if(PHASAR_LLVM_VERSION)
+ set(LLVM_HW_LLVM_VERSION ${PHASAR_LLVM_VERSION})
+endif()
+
+message(STATUS "llvm-hello-world: Use LLVM-${LLVM_HW_LLVM_VERSION}")
+
+find_package(LLVM ${LLVM_HW_LLVM_VERSION} REQUIRED CONFIG)
add_executable(main main.cpp)
target_link_libraries(main PRIVATE LLVMCore LLVMIRReader)
diff --git a/examples/llvm-hello-world/target/CMakeLists.txt b/examples/llvm-hello-world/target/CMakeLists.txt
index f8f25bef38..ef1be4701a 100644
--- a/examples/llvm-hello-world/target/CMakeLists.txt
+++ b/examples/llvm-hello-world/target/CMakeLists.txt
@@ -3,7 +3,9 @@ add_custom_target(LLFileGeneration ALL)
# Use phasar's capabilities to automate the LLVM-IR file generation
include(../../../cmake/phasar_macros.cmake)
-set(PHASAR_LLVM_VERSION 15)
+if (NOT PHASAR_LLVM_VERSION)
+ set(PHASAR_LLVM_VERSION 15)
+endif()
file(GLOB target_files RELATIVE ${CMAKE_CURRENT_LIST_DIR} *.cpp)
diff --git a/examples/use-phasar-with-fetch-content/CMakeLists.txt b/examples/use-phasar-with-fetch-content/CMakeLists.txt
index 026e29fcee..9195fc4fe5 100644
--- a/examples/use-phasar-with-fetch-content/CMakeLists.txt
+++ b/examples/use-phasar-with-fetch-content/CMakeLists.txt
@@ -11,7 +11,7 @@ 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 v2604
+ GIT_TAG development # At best, use a tagged version, such as v2610
EXCLUDE_FROM_ALL
OVERRIDE_FIND_PACKAGE
)
diff --git a/external/CRoaring b/external/CRoaring
new file mode 160000
index 0000000000..2e8395f1db
--- /dev/null
+++ b/external/CRoaring
@@ -0,0 +1 @@
+Subproject commit 2e8395f1dbf286d7944a7276195a7c40cbcbfd4a
diff --git a/include/phasar/ControlFlow/CFG.h b/include/phasar/ControlFlow/CFG.h
index 3200b0c17a..2efe0cb3b1 100644
--- a/include/phasar/ControlFlow/CFG.h
+++ b/include/phasar/ControlFlow/CFG.h
@@ -8,18 +8,21 @@
*****************************************************************************/
#pragma once
+#include "phasar/Utils/Nullable.h"
#include "phasar/Utils/TypeTraits.h"
#include "llvm/Support/raw_ostream.h"
#include
+#include
#include
namespace psr {
template
concept InstructionClassifier =
- requires(const T &IC, typename T::n_t Inst, typename T::n_t Succ) {
+ requires(const T &IC, typename std::remove_cvref_t::n_t Inst,
+ typename std::remove_cvref_t::n_t Succ) {
{ IC.isCallSite(Inst) } -> std::convertible_to;
{ IC.isFieldLoad(Inst) } -> std::convertible_to;
{ IC.isFieldStore(Inst) } -> std::convertible_to;
@@ -28,27 +31,36 @@ concept InstructionClassifier =
};
template
-concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
- typename T::n_t;
- typename T::f_t;
+concept CFG = requires(const T &CF, typename std::remove_cvref_t::n_t Inst,
+ typename std::remove_cvref_t::f_t Fun) {
+ typename std::remove_cvref_t::n_t;
+ typename std::remove_cvref_t::f_t;
/// Returns the function that contains the given instruction Inst.
// TODO: Actually belongs into ProjectIRDB!
- { CF.getFunctionOf(Inst) } -> std::convertible_to;
+ {
+ CF.getFunctionOf(Inst)
+ } -> std::convertible_to::f_t>;
/// Returns an iterable range of all instructions of the given function that
/// are part of the control-flow graph.
// TODO: We should have sth like this in the ProjectIRDB as well!
- { CF.getAllInstructionsOf(Fun) } -> psr::is_iterable_over_v;
+ {
+ CF.getAllInstructionsOf(Fun)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns an iterable range of all successor instructions of Inst in the
/// CFG.
/// NOTE: This function is typically being called in a hot part of the
/// analysis and should therefore be highly optimized for performance.
- { CF.getSuccsOf(Inst) } -> psr::is_iterable_over_v;
+ {
+ CF.getSuccsOf(Inst)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns an iterable range of all starting instructions of the given
/// function. For a forward-CFG, this is typically a singleton range.
- { CF.getStartPointsOf(Fun) } -> psr::is_iterable_over_v;
+ {
+ CF.getStartPointsOf(Fun)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns whether the given Inst is a root node of the CFG
{ CF.isStartPoint(Inst) } -> std::convertible_to;
@@ -59,32 +71,54 @@ concept CFG = requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
requires InstructionClassifier;
};
+template
+concept CFGOf =
+ CFG && std::same_as::n_t> &&
+ std::same_as::f_t>;
+
template
concept BidiCFG =
- CFG && requires(const T &CF, typename T::n_t Inst, typename T::f_t Fun) {
+ CFG && requires(const T &CF, typename std::remove_cvref_t::n_t Inst,
+ typename std::remove_cvref_t::f_t Fun) {
/// Returns an iterable range of all predecessor instructions of Inst in
/// the CFG
- { CF.getPredsOf(Inst) } -> psr::is_iterable_over_v;
+ {
+ CF.getPredsOf(Inst)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns an iterable range of all exit instructions (often return
/// instructions) of the given function. For a backward-CFG, this is
/// typically a singleton range
- { CF.getExitPointsOf(Fun) } -> psr::is_iterable_over_v;
+ {
+ CF.getExitPointsOf(Fun)
+ } -> psr::is_iterable_over_v::n_t>;
};
template
-concept CFGDump = requires(const T &CF, typename T::n_t Inst,
- typename T::f_t Fun, llvm::raw_ostream &OS) {
- { CF.getStatementId(Inst) } -> psr::is_string_like_v;
- { CF.getFunctionName(Fun) } -> psr::is_string_like_v;
- { CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
- CF.print(Fun, OS);
-};
+concept CFGDump =
+ requires(const T &CF, typename std::remove_cvref_t::n_t Inst,
+ typename std::remove_cvref_t::f_t Fun, llvm::raw_ostream &OS) {
+ { CF.getStatementId(Inst) } -> psr::is_string_like_v;
+ { CF.getFunctionName(Fun) } -> psr::is_string_like_v;
+ { CF.getDemangledFunctionName(Fun) } -> psr::is_string_like_v;
+ CF.print(Fun, OS);
+ };
template
-concept CFGEdgesProvider = requires(const T &CF, typename T::f_t Fun) {
+concept CFGEdgesProvider = requires(const T &CF,
+ typename std::remove_cvref_t::f_t Fun) {
{
CF.getAllControlFlowEdges(Fun)
- } -> psr::is_iterable_over_v>;
+ } -> psr::is_iterable_over_v::n_t,
+ typename std::remove_cvref_t::n_t>>;
};
+
+template
+concept IsBlockAwareControlFlow =
+ requires(const T &CF, typename std::remove_cvref_t::n_t Inst) {
+ {
+ CF.getUniqueSuccessor(Inst)
+ } -> std::convertible_to::n_t>>;
+ { CF.hasUniquePredecessor(Inst) } -> std::convertible_to;
+ };
} // namespace psr
diff --git a/include/phasar/ControlFlow/CGSCCs.h b/include/phasar/ControlFlow/CGSCCs.h
new file mode 100644
index 0000000000..5afe66dc53
--- /dev/null
+++ b/include/phasar/ControlFlow/CGSCCs.h
@@ -0,0 +1,164 @@
+#pragma once
+
+/******************************************************************************
+ * Copyright (c) 2026 Fabian Schiebel.
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of LICENSE.txt.
+ *
+ * Contributors:
+ * Fabian Schiebel and others
+ *****************************************************************************/
+
+#include "phasar/ControlFlow/CFG.h"
+#include "phasar/ControlFlow/CallGraph.h"
+#include "phasar/ControlFlow/ICFG.h"
+#include "phasar/Utils/BitSet.h"
+#include "phasar/Utils/FunctionId.h"
+#include "phasar/Utils/IotaIterator.h"
+#include "phasar/Utils/SCCGeneric.h"
+
+namespace psr {
+
+// TODO: Use SCCGeneric algorithms here!
+
+// Note: Use forward edges (i.e., cs->callee), such that the SCC-order reflects
+// the bottom-up iteration order.
+template C>
+ requires InstructionClassifier
+[[nodiscard]] SCCHolder
+computeCGSCCs(const psr::CallGraph &CG, const C &CF,
+ const FunctionCompressor &Functions) {
+
+ SCCHolder Ret{};
+
+ auto NumFuns = Functions.size();
+ if (!NumFuns) {
+ return Ret;
+ }
+
+ Ret.SCCOfNode.resize(NumFuns);
+
+ llvm::SmallVector Disc(NumFuns, UINT32_MAX);
+ llvm::SmallVector Low(NumFuns, UINT32_MAX);
+ BitSet OnStack(NumFuns);
+ BitSet Seen(NumFuns);
+
+ llvm::SmallVector Stack;
+ uint32_t Time = 0;
+
+ constexpr auto SetMin = [](uint32_t &InOut, uint32_t Other) {
+ if (Other < InOut) {
+ InOut = Other;
+ }
+ };
+
+ const auto Dfs = [&](auto &&Dfs, FunctionId CurrNode) -> void {
+ auto CurrTime = Time++;
+ Disc[size_t(CurrNode)] = CurrTime;
+ Low[size_t(CurrNode)] = CurrTime;
+ Stack.push_back(CurrNode);
+ OnStack.insert(CurrNode);
+
+ const auto &CurrFun = Functions[CurrNode];
+ for (const auto &Inst : CF.getAllInstructionsOf(CurrFun)) {
+ if (!CF.isCallSite(Inst)) {
+ continue;
+ }
+
+ for (const auto &Succ : CG.getCalleesOfCallAt(Inst)) {
+ auto SuccNode = Functions.get(Succ);
+ if (Disc[size_t(SuccNode)] == UINT32_MAX) {
+ // Tree-edge: Not seen yet --> recurse
+
+ Dfs(Dfs, SuccNode);
+ SetMin(Low[size_t(CurrNode)], Low[size_t(SuccNode)]);
+ } else if (OnStack.contains(SuccNode)) {
+ // Back-edge --> circle!
+
+ SetMin(Low[size_t(CurrNode)], Disc[size_t(SuccNode)]);
+ }
+ }
+ }
+
+ if (Low[size_t(CurrNode)] == Disc[size_t(CurrNode)]) {
+ // Found SCC
+
+ auto SCCIdx = SCCId(Ret.NodesInSCC.size());
+ auto &FunsInSCC = Ret.NodesInSCC.emplace_back();
+
+ assert(!Stack.empty());
+
+ while (Stack.back() != CurrNode) {
+ auto Fun = Stack.pop_back_val();
+ Ret.SCCOfNode[Fun] = SCCIdx;
+ OnStack.erase(Fun);
+ Seen.insert(Fun);
+ FunsInSCC.push_back(Fun);
+ }
+
+ auto Fun = Stack.pop_back_val();
+ Ret.SCCOfNode[Fun] = SCCIdx;
+ OnStack.erase(Fun);
+ Seen.insert(Fun);
+ FunsInSCC.push_back(Fun);
+ }
+ };
+
+ for (auto FunId : iota(NumFuns)) {
+ if (!Seen.contains(FunId)) {
+ Dfs(Dfs, FunId);
+ }
+ }
+
+ return Ret;
+}
+
+template
+ requires InstructionClassifier
+[[nodiscard]] SCCHolder
+computeCGSCCs(const I &ICF,
+ const FunctionCompressor &Functions) {
+ return computeCGSCCs(ICF.getCallGraph(), ICF, Functions);
+}
+
+template
+[[nodiscard]] SCCDependencyGraph
+computeCGSCCCallers(const psr::CallGraph &CG, const CFGOf auto &CF,
+ const FunctionCompressor &Functions,
+ const SCCHolder &SCCs) {
+ SCCDependencyGraph Ret;
+ Ret.ChildrenOfSCC.resize(SCCs.size());
+
+ BitSet> Leaves(SCCs.size(), true);
+
+ for (auto [FunId, Fun] : Functions.enumerate()) {
+ auto SCC = SCCs.SCCOfNode[FunId];
+
+ for (const auto &CS : CG.getCallersOf(Fun)) {
+ const auto &CSFun = CF.getFunctionOf(CS);
+ if (auto CSFunId = Functions.getOrNull(CSFun)) {
+ auto CSFunSCC = SCCs.SCCOfNode[*CSFunId];
+ if (CSFunSCC != SCC) {
+ Ret.ChildrenOfSCC[SCC].insert(CSFunSCC);
+ Leaves.erase(CSFunSCC);
+ }
+ }
+ }
+ }
+
+ Ret.SCCRoots.reserve(Leaves.size());
+ Leaves.foreach (
+ [&](auto Leaf) { Ret.SCCRoots.push_back(SCCId(Leaf)); });
+
+ return Ret;
+}
+
+template
+[[nodiscard]] SCCDependencyGraph
+computeCGSCCCallers(const I &ICF,
+ const FunctionCompressor &Functions,
+ const SCCHolder &SCCs) {
+ return computeCGSCCCallers(ICF.getCallGraph(), ICF, Functions, SCCs);
+}
+
+} // namespace psr
diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h
index 67cdc04e38..59409fd82b 100644
--- a/include/phasar/ControlFlow/CallGraph.h
+++ b/include/phasar/ControlFlow/CallGraph.h
@@ -17,8 +17,10 @@
#include "phasar/Utils/StableVector.h"
#include "phasar/Utils/Utilities.h"
+#include "llvm/ADT/ArrayRef.h"
#include "llvm/IR/Function.h"
+#include
#include
#include
#include
@@ -52,6 +54,24 @@ class CallGraph : public CallGraphBase> {
/// Creates a new, empty call-graph
CallGraph() noexcept = default;
+ auto
+ iterateCalleesView(std::invocable> auto Fn) const {
+ return psr::foreachInRange(
+ CalleesAt, [Fn = copyOrRef(Fn)](const auto &Elem) {
+ const auto &[CS, Callees] = Elem;
+ return psr::invokeControlFlow(Fn, CS, llvm::ArrayRef(*Callees));
+ });
+ }
+
+ auto
+ iterateCallersView(std::invocable> auto Fn) const {
+ return psr::foreachInRange(
+ CallersOf, [Fn = copyOrRef(Fn)](const auto &Elem) {
+ const auto &[CS, Callers] = Elem;
+ return psr::invokeControlFlow(Fn, CS, llvm::ArrayRef(*Callers));
+ });
+ }
+
/// Deserializes a previously computed call-graph
template
[[nodiscard]] static CallGraph
diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h
index 2d39581e3c..186ca426f2 100644
--- a/include/phasar/ControlFlow/CallGraphBase.h
+++ b/include/phasar/ControlFlow/CallGraphBase.h
@@ -15,6 +15,7 @@
#include "phasar/Utils/Compressor.h"
#include "phasar/Utils/GraphTraits.h"
#include "phasar/Utils/IotaIterator.h"
+#include "phasar/Utils/Macros.h"
#include "phasar/Utils/NonNullPtr.h"
#include "phasar/Utils/TypeTraits.h"
@@ -147,13 +148,15 @@ template class ReverseCGGraph {
typename CallGraphTy::f_t>;
constexpr ReverseCGGraph(
- NonNullPtr CGView, NonNullPtr IRDB,
+ NonNullPtr CGView PSR_LIFETIMEBOUND,
+ NonNullPtr IRDB PSR_LIFETIMEBOUND,
Compressor FC) noexcept
requires(NeedsMapping)
: CGView(CGView), IRDB(IRDB), FC(std::move(FC)) {}
- constexpr ReverseCGGraph(NonNullPtr CGView,
- NonNullPtr IRDB) noexcept
+ constexpr ReverseCGGraph(NonNullPtr CGView
+ PSR_LIFETIMEBOUND,
+ NonNullPtr IRDB PSR_LIFETIMEBOUND) noexcept
: CGView(CGView), IRDB(IRDB) {
FC.reserve(CGView->getNumVertexFunctions());
for (const auto &Fun : CGView->getAllVertexFunctions()) {
diff --git a/include/phasar/ControlFlow/ControlFlowOrder.h b/include/phasar/ControlFlow/ControlFlowOrder.h
new file mode 100644
index 0000000000..06751b1471
--- /dev/null
+++ b/include/phasar/ControlFlow/ControlFlowOrder.h
@@ -0,0 +1,72 @@
+#pragma once
+
+/******************************************************************************
+ * Copyright (c) 2026 Fabian Schiebel.
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of LICENSE.txt.
+ *
+ * Contributors:
+ * Fabian Schiebel and others
+ *****************************************************************************/
+
+#include "phasar/ControlFlow/CFG.h"
+#include "phasar/Utils/ByRef.h"
+#include "phasar/Utils/Compressor.h"
+#include "phasar/Utils/Nullable.h"
+#include "phasar/Utils/Utilities.h"
+
+#include "llvm/ADT/DenseSet.h"
+
+#include
+
+namespace psr {
+
+template struct ControlFlowOrder {
+ enum class CFGOrderId : uint32_t {};
+
+ Compressor Order;
+
+ [[nodiscard]] auto begin() const noexcept {
+ return std::make_reverse_iterator(Order.begin());
+ }
+ [[nodiscard]] auto end() const noexcept {
+ return std::make_reverse_iterator(Order.end());
+ }
+};
+
+template CFGTy>
+void computeCFGOrder(ControlFlowOrder &Into, const CFGTy &CF, const F &Fun) {
+ llvm::SmallDenseSet Seen;
+
+ const auto Visit = [&](auto &Visit, ByConstRef Inst) {
+ if (!Seen.insert(Inst).second) {
+ return;
+ }
+
+ scope_exit Push = [&]() { Into.Order.insert(Inst); };
+
+ if constexpr (IsBlockAwareControlFlow) {
+ if (auto Next = CF.getUniqueSuccessor(Inst)) {
+ Visit(Visit, unwrapNullable(Next));
+ return;
+ }
+ }
+ for (const auto &Succ : CF.getSuccsOf(Inst)) {
+ Visit(Visit, Succ);
+ }
+ };
+
+ for (const auto &SP : CF.getStartPointsOf(Fun)) {
+ Visit(Visit, SP);
+ }
+}
+
+template CFGTy>
+[[nodiscard]] inline ControlFlowOrder computeCFGOrder(const CFGTy &CF,
+ const F &Fun) {
+ ControlFlowOrder Ret;
+ computeCFGOrder(Ret, CF, Fun);
+ return Ret;
+}
+
+} // namespace psr
diff --git a/include/phasar/ControlFlow/ICFG.h b/include/phasar/ControlFlow/ICFG.h
index d3660fe11f..e4697e74f6 100644
--- a/include/phasar/ControlFlow/ICFG.h
+++ b/include/phasar/ControlFlow/ICFG.h
@@ -8,6 +8,7 @@
*****************************************************************************/
#pragma once
+#include "phasar/ControlFlow/CFG.h"
#include "phasar/ControlFlow/CallGraphBase.h"
#include "phasar/Utils/Nullable.h"
#include "phasar/Utils/TypeTraits.h"
@@ -16,18 +17,24 @@
#include "llvm/Support/raw_ostream.h"
#include
+#include
namespace psr {
template
-concept ICFG = requires(const T &ICF, llvm::StringRef Name,
- typename T::n_t Inst, typename T::f_t Fun) {
- typename T::f_t;
- typename T::n_t;
+concept ICFG = CFG && requires(const T &ICF, llvm::StringRef Name,
+ typename std::remove_cvref_t::n_t Inst,
+ typename std::remove_cvref_t::f_t Fun) {
+ typename std::remove_cvref_t::f_t;
+ typename std::remove_cvref_t::n_t;
// TODO: Should not be duplicated with ProjectIRDB
- { ICF.getAllFunctions() } -> is_iterable_over_v;
+ {
+ ICF.getAllFunctions()
+ } -> is_iterable_over_v::f_t>;
// TODO: Should not be duplicated with ProjectIRDB
- { ICF.getFunction(Name) } -> std::convertible_to>;
+ {
+ ICF.getFunction(Name)
+ } -> std::convertible_to::f_t>>;
{ ICF.isIndirectFunctionCall(Inst) } -> std::convertible_to;
{ ICF.isVirtualFunctionCall(Inst) } -> std::convertible_to;
@@ -37,19 +44,27 @@ concept ICFG = requires(const T &ICF, llvm::StringRef Name,
/// Returns an iterable range of all possible callee candidates at the given
/// call-site induced by the used call-graph. Same as
/// getCallGraph().getCalleesOfCallAt(Inst)
- { ICF.getCalleesOfCallAt(Inst) } -> psr::is_iterable_over_v;
+ {
+ ICF.getCalleesOfCallAt(Inst)
+ } -> psr::is_iterable_over_v::f_t>;
/// Returns an iterable range of all possible call-site candidates that may
/// call the given function induced by the used call-graph. Same as
/// getCallGraph().getCallersOf(Fun)
- { ICF.getCallersOf(Fun) } -> psr::is_iterable_over_v;
+ {
+ ICF.getCallersOf(Fun)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns an iterable range of all call-instruction in the given function
- { ICF.getCallsFromWithin(Fun) } -> psr::is_iterable_over_v;
+ {
+ ICF.getCallsFromWithin(Fun)
+ } -> psr::is_iterable_over_v::n_t>;
/// Returns an iterable range of all instructions in all functions of the ICFG
/// that are neither call-sites nor start-points of a function
// TODO: Get rid of this function
- { ICF.allNonCallStartNodes() } -> psr::is_iterable_over_v;
+ {
+ ICF.allNonCallStartNodes()
+ } -> psr::is_iterable_over_v::n_t>;
/// The total number of call-sites in the ICFG. Same as
/// getCallGraph().getNumVertexCallSites()
@@ -61,4 +76,9 @@ concept ICFGDump = requires(const T &ICF, llvm::raw_ostream &OS) {
ICF.print(OS);
ICF.printAsJson(OS);
};
+
+template
+concept ICFGOf =
+ ICFG && std::same_as::n_t> &&
+ std::same_as::f_t>;
} // namespace psr
diff --git a/include/phasar/ControlFlow/SparseCFGBase.h b/include/phasar/ControlFlow/SparseCFGBase.h
index 074f829d04..91a9732806 100644
--- a/include/phasar/ControlFlow/SparseCFGBase.h
+++ b/include/phasar/ControlFlow/SparseCFGBase.h
@@ -34,7 +34,7 @@ template class SparseCFGBase : public CFGBase {
template
// NOLINTNEXTLINE(readability-identifier-naming)
-constexpr bool is_sparse_cfg_v =
+concept is_sparse_cfg_v =
is_crtp_base_of_v &&
std::is_same_v &&
std::is_same_v;
diff --git a/include/phasar/ControlFlow/SparseCFGProvider.h b/include/phasar/ControlFlow/SparseCFGProvider.h
index f85255e2ee..cdc2515f97 100644
--- a/include/phasar/ControlFlow/SparseCFGProvider.h
+++ b/include/phasar/ControlFlow/SparseCFGProvider.h
@@ -12,6 +12,7 @@
#include "phasar/Utils/ByRef.h"
+#include
#include
namespace psr {
@@ -38,11 +39,19 @@ template class SparseCFGProvider {
};
template
-// NOLINTNEXTLINE
-constexpr bool has_getSparseCFG_v =
+concept has_getSparseCFG_v =
requires(const T &ICF, typename T::f_t Fun, D Fact) { //
ICF.getSparseCFG(Fun, Fact);
};
+
+template
+concept has_advanceToNextUser_v =
+ requires(const T &ICF, typename T::n_t Inst, D Fact) {
+ {
+ ICF.advanceToNextUser(Inst, Fact)
+ } -> std::convertible_to;
+ };
+
} // namespace psr
#endif // PHASAR_CONTROLFLOW_SPARSECFGPROVIDER_H
diff --git a/include/phasar/DB.h b/include/phasar/DB.h
index 294b4e2bb6..430bd5786b 100644
--- a/include/phasar/DB.h
+++ b/include/phasar/DB.h
@@ -12,6 +12,5 @@
#include "phasar/Config/phasar-config.h"
#include "phasar/DB/ProjectIRDB.h"
-#include "phasar/DB/ProjectIRDBBase.h"
#endif // PHASAR_DB_H
diff --git a/include/phasar/DB/ProjectIRDB.h b/include/phasar/DB/ProjectIRDB.h
index c630be0765..290c6fa83e 100644
--- a/include/phasar/DB/ProjectIRDB.h
+++ b/include/phasar/DB/ProjectIRDB.h
@@ -14,6 +14,7 @@
#include "llvm/ADT/StringRef.h"
#include
+#include
namespace psr {
@@ -111,4 +112,19 @@ concept ProjectIRDB =
requires ProjectSymbolTable;
};
+
+template
+concept ProjectIRDBPtr =
+ ProjectIRDB())>>;
+template
+concept ProjectIRDBConstPtr =
+ std::is_const_v())>> &&
+ ProjectIRDB())>>;
+
+// NOLINTNEXTLINE(readability-identifier-naming)
+auto IRDBGetFunctionDef(ProjectIRDB auto *IRDB) noexcept {
+ return [IRDB](llvm::StringRef Name) {
+ return IRDB->getFunctionDefinition(Name);
+ };
+}
} // namespace psr
diff --git a/include/phasar/DB/ProjectIRDBBase.h b/include/phasar/DB/ProjectIRDBBase.h
deleted file mode 100644
index 0fccf84f0d..0000000000
--- a/include/phasar/DB/ProjectIRDBBase.h
+++ /dev/null
@@ -1,194 +0,0 @@
-/******************************************************************************
- * Copyright (c) 2022 Philipp Schubert.
- * All rights reserved. This program and the accompanying materials are made
- * available under the terms of LICENSE.txt.
- *
- * Contributors:
- * Fabian Schiebel and others
- *****************************************************************************/
-
-#ifndef PHASAR_DB_PROJECTIRDBBASE_H
-#define PHASAR_DB_PROJECTIRDBBASE_H
-
-#include "phasar/Utils/ByRef.h"
-#include "phasar/Utils/Nullable.h"
-#include "phasar/Utils/TypeTraits.h"
-
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Debug.h"
-
-namespace psr {
-template struct ProjectIRDBTraits {
- // using n_t
- // using f_t
- // using m_t
- // using g_t
-};
-
-/// This class owns the IR code of the target being analyzed and some
-/// very important information associated with the IR.
-/// When an object of this class is destroyed it will clean up all IR related
-/// stuff that is stored in it.
-///
-/// m_t - module type
-/// f_t - function type
-/// n_t - instruction type
-/// g_t - global variable type
-///
-/// \remark XXX Once we have upgraded to C++20, we might want to use a concept
-/// instead...
-template
-class LLVM_DEPRECATED(
- "This CRTP mixin is deprecated in favor of the ProjectIRDB-concept",
- "Use ProjectIRDB instead") ProjectIRDBBase {
-public:
- using n_t = typename ProjectIRDBTraits::n_t;
- using f_t = typename ProjectIRDBTraits::f_t;
- using m_t = typename ProjectIRDBTraits::m_t;
- using g_t = typename ProjectIRDBTraits::g_t;
-
- /// Returns the managed module
- [[nodiscard]] m_t getModule() const noexcept {
- assert(isValid());
- return self().getModuleImpl();
- }
-
- /// Check if debug information are available.
- [[nodiscard]] bool debugInfoAvailable() const {
- assert(isValid());
- return self().debugInfoAvailableImpl();
- }
-
- /// Returns a range of all function definitions and declarations available
- [[nodiscard]] decltype(auto) getAllFunctions() const {
- static_assert(
- is_iterable_over_v);
- assert(isValid());
- return self().getAllFunctionsImpl();
- }
-
- // Returns the function if available, nullptr/nullopt otherwise.
- [[nodiscard]] Nullable getFunction(llvm::StringRef FunctionName) const {
- assert(isValid());
- return self().getFunctionImpl(FunctionName);
- }
- /// Returns the function's definition if available, null otherwise.
- [[nodiscard]] Nullable
- getFunctionDefinition(llvm::StringRef FunctionName) const {
- assert(isValid());
- return self().getFunctionDefinitionImpl(FunctionName);
- }
- /// Returns whether the IRDB contains a function with the given name.
- [[nodiscard]] bool hasFunction(llvm::StringRef FunctionName) const noexcept {
- assert(isValid());
- return self().hasFunctionImpl(FunctionName);
- }
-
- /// Returns the function that contains the given instruction Inst.
- /// Each instruction must be part of a function.
- [[nodiscard]] f_t getFunctionOf(ByConstRef Inst) const {
- assert(isValid());
- return self().getFunctionOfImpl(Inst);
- }
-
- /// Returns the global variable if available, nullptr/nullopt
- /// otherwise.
- [[nodiscard]] Nullable
- getGlobalVariable(llvm::StringRef GlobalVariableName) const {
- assert(isValid());
- return self().getGlobalVariableImpl(GlobalVariableName);
- }
-
- /// Returns the global variable's definition if available, nullptr/nullopt
- /// otherwise.
- [[nodiscard]] Nullable
- getGlobalVariableDefinition(llvm::StringRef GlobalVariableName) const {
- assert(isValid());
- return self().getGlobalVariableDefinitionImpl(GlobalVariableName);
- }
-
- /// Returns the number of instruction in the managed module.
- [[nodiscard]] size_t getNumInstructions() const noexcept {
- assert(isValid());
- return self().getNumInstructionsImpl();
- }
- /// Returns the number of global variables in the managed module.
- [[nodiscard]] size_t getNumGlobals() const noexcept {
- assert(isValid());
- return self().getNumGlobalsImpl();
- }
- /// Returns the number of functions in the managed module.
- [[nodiscard]] size_t getNumFunctions() const noexcept {
- assert(isValid());
- return self().getNumFunctionsImpl();
- }
-
- /// Returns the instruction to the corresponding Id. Returns nullptr/nullopt,
- /// if there is no instruction for this Id
- [[nodiscard]] Nullable getInstruction(size_t Id) const {
- assert(isValid());
- return self().getInstructionImpl(Id);
- }
- /// Returns an instruction's ID. The instruction must belong to the managed
- /// module for this function to work
- [[nodiscard]] size_t getInstructionId(n_t Inst) const {
- assert(isValid());
- return self().getInstructionIdImpl(Inst);
- }
-
- [[nodiscard]] decltype(auto) getAllInstructions() const {
- static_assert(
- is_iterable_over_v);
- assert(isValid());
- return self().getAllInstructionsImpl();
- }
-
- /// Sanity check to verify that th IRDB really manages a Module and all
- /// functions work properly.
- /// All functions of this IRDB require isValid() to return true, if not stated
- /// differently -- otherwise, they must not be called.
- /// This function (obviously) does not require isValid() to return true.
- [[nodiscard]] bool isValid() const noexcept { return self().isValidImpl(); }
-
- /// Dumps the managed module to llvm::dbgs() if isValid(); otherwise prints
- /// "".
- /// This function can be invoked from a debugger.
- LLVM_DUMP_METHOD void dump() const {
- if (!isValid()) {
- llvm::dbgs() << "\n";
- llvm::dbgs().flush();
- return;
- }
- self().dumpImpl();
- }
-
- ProjectIRDBBase(const ProjectIRDBBase &) = delete;
- ProjectIRDBBase &operator=(const ProjectIRDBBase &) = delete;
-
- ProjectIRDBBase(ProjectIRDBBase &&) noexcept = default;
- ProjectIRDBBase &operator=(ProjectIRDBBase &&) noexcept = default;
-
- ~ProjectIRDBBase() = default;
-
-private:
- friend Derived;
- ProjectIRDBBase() noexcept = default;
-
- Derived &self() noexcept { return static_cast(*this); }
- const Derived &self() const noexcept {
- return static_cast(*this);
- }
-};
-
-template
-// NOLINTNEXTLINE(readability-identifier-naming)
-auto IRDBGetFunctionDef(const ProjectIRDBBase *IRDB) noexcept {
- return [IRDB](llvm::StringRef Name) {
- return IRDB->getFunctionDefinition(Name);
- };
-}
-
-} // namespace psr
-
-#endif // PHASAR_DB_PROJECTIRDBBASE_H
diff --git a/include/phasar/DataFlow.h b/include/phasar/DataFlow.h
index 5cd7522f42..686f075db4 100644
--- a/include/phasar/DataFlow.h
+++ b/include/phasar/DataFlow.h
@@ -17,8 +17,12 @@
#include "phasar/DataFlow/IfdsIde/EdgeFunctions.h"
#include "phasar/DataFlow/IfdsIde/EntryPointUtils.h"
#include "phasar/DataFlow/IfdsIde/FlowFunctions.h"
+#include "phasar/DataFlow/IfdsIde/IDEProblem.h"
+#include "phasar/DataFlow/IfdsIde/IDEProblemWrapper.h"
#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h"
+#include "phasar/DataFlow/IfdsIde/IFDSProblem.h"
#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h"
+#include "phasar/DataFlow/IfdsIde/IfdsToIdeProblemAdapter.h"
#include "phasar/DataFlow/IfdsIde/InitialSeeds.h"
#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h"
#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h"
@@ -32,6 +36,13 @@
#include "phasar/DataFlow/Mono/IntraMonoProblem.h"
#include "phasar/DataFlow/Mono/Solver/InterMonoSolver.h"
#include "phasar/DataFlow/Mono/Solver/IntraMonoSolver.h"
+#include "phasar/DataFlow/MonoIfds/ArraySetWorkList.h"
+#include "phasar/DataFlow/MonoIfds/DataFlowEnvironment.h"
+#include "phasar/DataFlow/MonoIfds/IterationStrategy.h"
+#include "phasar/DataFlow/MonoIfds/MonoIFDSConfig.h"
+#include "phasar/DataFlow/MonoIfds/MonoIFDSProblem.h"
+#include "phasar/DataFlow/MonoIfds/MonoIFDSSolver.h"
+#include "phasar/DataFlow/MonoIfds/RPOWorkList.h"
#include "phasar/DataFlow/PathSensitivity/PathSensitivityConfig.h"
#include "phasar/DataFlow/PathSensitivity/PathSensitivityManager.h"
diff --git a/include/phasar/DataFlow/HelperAnalyses.h b/include/phasar/DataFlow/HelperAnalyses.h
new file mode 100644
index 0000000000..2efe94a7d4
--- /dev/null
+++ b/include/phasar/DataFlow/HelperAnalyses.h
@@ -0,0 +1,41 @@
+#pragma once
+
+/******************************************************************************
+ * Copyright (c) 2026 Fabian Schiebel.
+ * All rights reserved. This program and the accompanying materials are made
+ * available under the terms of LICENSE.txt.
+ *
+ * Contributors:
+ * Fabian Schiebel and others
+ *****************************************************************************/
+
+#include "phasar/ControlFlow/ICFG.h"
+#include "phasar/Utils/FunctionId.h"
+#include "phasar/Utils/SCCGeneric.h"
+
+#include
+
+namespace psr {
+template
+concept CanGetICFG = requires(T &HA) {
+ { HA.getICFG() } -> ICFG;
+};
+
+template
+concept CanGetICFGOf = requires(T &HA) {
+ { HA.getICFG() } -> ICFGOf