Skip to content

Conversation

@Johanmyst
Copy link
Contributor

📝 Overview

This PR consolidates six orthogonal areas of work into one release:

  • Build System: make installs relocatable, expose ABI flags, support in- and out-of-tree usage
  • CI/Workflow: harden coverage removal flags in GitHub Actions
  • CMake Formatting: add cmake-format config for consistent styling & formatting
  • API Additions: add missing has…() checks alongside existing getters
  • WPA Backends: unify public interface for polymorphic use
  • SVFG Builder: fix read/write order for optimized graphs

For full details on each change, see the linked commits below.

If you'd like, I could add a section to the setup guide wiki page to explain installing & using SVF's CMake packages. Additionally, I could also add a new page detailing how to use SVF from within an out-of-tree LLVM pass directly from the default clang pass-pipeline (i.e., analyse & possibly modify a module using information from SVF's analyses during regular compilation (by simply adding a --Wl,--load-pass-plugin=libSVFUser.so)?


🚀 Highlights

Area Summary Commit
Build System Relocatable installs, ABI-flag interface, in-/out-of-tree mode 583bff0
CI/Workflow Uniform --ignore-errors unused on all lcov --remove 96c021b
CMake Formatting Add cmake-format spec for automatic styling 0bc1472
API Existence New has…() functions for previously unchecked getters ce288cf
WPA Polymorphism Publicise ctor/read/dump APIs in VersionedFlowSensitive 93446f6
SVFG Builder Write unoptimised graph before optimise; explicit flag def7c04

⚙️ Detailed Changes

1️⃣ Build System ([583bff0])

  • Directory layout: moved all CMake templates into cmake/
  • Relocatable install: properly support relocation correctly (cmake --install … --install-prefix=<prefix>)
  • ABI flags: expose minimum C++ standard, PIC, etc., via an (invisible) interface library
  • pkgconfig export: generate .pc with core flags (less complete than the CMake package)
  • In-tree usage: add_subdirectory(SVF) now works transparently
  • LTO guard: error out if compiler doesn’t support LTO
  • Dependency flags: propagate Z3/LLVM ABI flags on SVF::Core & SVF::LLVM
  • Conditional version export: only set SVF_VERSION in parent scope when parent CMake project exists

2️⃣ CI/Workflow Fix ([96c021b])

  • Always pass --ignore-errors unused to all lcov --remove … calls
  • Prevent workflow failures when no coverage data exists under /usr/* or other paths
  • Ensured error suppression is applied consistently

3️⃣ Automated CMake Formatting ([0bc1472])

  • Add .cmake-format.py so cmake-format can lint & format all CMake files
  • Ensures consistent styling across CMakeLists.txt, *.cmake, etc.

4️⃣ Existence-Check APIs ([ce288cf])

  • Introduce hasLLVMValue(), hasLHSTopLevelPtr(), etc., for getters that assert on missing entries
  • Avoids breaking existing API: getters still assert, but you can pre-check
  • Getters that return nullptr on missing entries are unmodified

5️⃣ WPA Backend Polymorphism ([93446f6])

  • Mark VersionedFlowSensitive’s printStat(), dumpTopLevelPtsTo(), solveAndWritePtsToFile(), readPtsFromFile(), etc., as public
  • Enables code like:
    SVF::BVDataPTAImpl *pta = nullptr;
    switch(backend) {
      case Andersen_BASE:   pta = new Andersen(...);            break;
      case VFS_WPA:         pta = new VersionedFlowSensitive(...); break;
      default:              /**/;                              break;
    }
    

6️⃣ Optimised SVFG Read/Write Fix ([def7c04])

  • Add builder argument to build optimised SVFG without command-line option (to support using SVF as a library)
  • Write unoptimised SVFG before optimisation
  • Read unoptimised SVFG, then apply optimisation
  • Resolves crashes when reading a post-optimised file

@codecov
Copy link

codecov bot commented May 2, 2025

Codecov Report

Attention: Patch coverage is 30.00000% with 14 lines in your changes missing coverage. Please review.

Project coverage is 63.67%. Comparing base (9efda8a) to head (d25a348).
Report is 2 commits behind head on master.

Files with missing lines Patch % Lines
svf/lib/Graphs/SVFGOPT.cpp 0.00% 12 Missing ⚠️
svf/lib/MSSA/SVFGBuilder.cpp 75.00% 1 Missing ⚠️
svf/lib/Util/ExtAPI.cpp 50.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1697      +/-   ##
==========================================
- Coverage   63.70%   63.67%   -0.04%     
==========================================
  Files         244      244              
  Lines       25962    25988      +26     
  Branches     4565     4581      +16     
==========================================
+ Hits        16539    16547       +8     
- Misses       9423     9441      +18     
Files with missing lines Coverage Δ
svf-llvm/include/SVF-LLVM/LLVMModule.h 97.75% <ø> (+0.02%) ⬆️
svf/include/Graphs/SVFGOPT.h 0.00% <ø> (ø)
svf/include/Graphs/VFG.h 90.72% <ø> (ø)
svf/include/MSSA/SVFGBuilder.h 100.00% <100.00%> (ø)
svf/include/WPA/VersionedFlowSensitive.h 100.00% <ø> (ø)
svf/lib/MSSA/SVFGBuilder.cpp 84.21% <75.00%> (-1.91%) ⬇️
svf/lib/Util/ExtAPI.cpp 39.32% <50.00%> (-2.25%) ⬇️
svf/lib/Graphs/SVFGOPT.cpp 0.00% <0.00%> (ø)

... and 13 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@yuleisui
Copy link
Collaborator

yuleisui commented May 2, 2025

Thanks for the contribution. Could you split this pull request into two?

  1. Build system
  2. All others

I had a quick look at 2, all look good, and should be ready to merge.

1 looks a bit complicated and I will have to go through it separately.

Cheers

@Johanmyst
Copy link
Contributor Author

Thanks for the contribution. Could you split this pull request into two?

Sure, give me a bit to figure out what the easiest way to do this would be!

I had a quick look at 2, all look good, and should be ready to merge.

Great, happy to hear!

1 looks a bit complicated and I will have to go through it separately.

Makes sense. I did make sure to not break any backwards compatibility; all current ways of using SVF should continue to work unchanged. The only difference is that the exported CMake package now properly handles relocatability and dependency inheritance.

For example, the current CMake package doesn't properly expose dependencies and necessary ABI flags on the actual targets. As a result, linking against SVF's package after finding system instances (e.g., with find_package(SVF)) can still experience issues with this.

For example, say I want to use system-wide installations of Z3 & LLVM to build & link SVF against. I could install Z3 & LLVM like this:

# Z3 from official Ubuntu repositories
> sudo apt install z3 libz3-4 libz3-dev

# Ubuntu 24.04 (Noble) has LLVM-16 in the official repos as well
> sudo apt install llvm-16 llvm-16-dev llvm-16-tools \
    clang-16 libclang-16-dev libclang-rt-16-dev \
    lld-16 liblld-16 liblld-16-dev \
    ...

This would install these libraries in /usr/... and into the default paths such that any CMakeLists.txt script could do find_package(Z3) and find_package(LLVM) to find their CMake packages. To link against them --- and automatically inherit all required dependencies, ABI flags, etc. --- one can simply call target_link_libraries(myLib PRIVATE libz3).

These changes support using SVF in the same way. For example, I can build & install SVF globally like this:

> git clone https://github.com/SVF-tools/SVF.git ./SVF
> cd SVF
> cmake -S ./ \
    -G "Ninja" \
    -B Release-build \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_INSTALL_PREFIX=/usr/local \
    -DSVF_ENABLE_ASSERTIONS=ON \
    -DSVF_EXPORT_DYNAMIC=ON \
    -DBUILD_SHARED_LIBS=ON \
    -DSVF_DEBUG_INFO=ON \
    -DSVF_USE_LLD=ON
> cmake --build Release-build
> cmake --install Release-build [--install-prefix=/some/other/prefix]

This would install SVF's headers, CMake package, etc. to the chosen prefix so that the tools end up in places like /usr/local/bin/wpa and /usr/local/lib/libSvfCore.so. If I now wanted to use this global SVF instance in another project, I could simply find & link it in the same way SVF finds LLVM and Z3:

// File: CMakeLists.txt:

cmake_minimum_required(VERSION 3.28)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

project(Foo CXX)
add_executable(foo foo.cpp)
find_package(SVF 3.0 REQUIRED CONFIG)
target_link_libraries(foo PRIVATE SVF::SvfCore SVF::SvfLLVM)
install(TARGETS foo RUNTIME DESTINATION bin)

// File: foo.cpp:

#include <MSSA/SVFGBuilder.h>
#include <SVF/Graphs/SVFG.h>
#include <SVF/SVF-LLVM/LLVMModule.h>
#include <SVF/SVF-LLVM/SVFIRBuilder.h>
#include <SVF/Util/CommandLine.h>
#include <SVF/Util/Options.h>
#include <SVF/WPA/Andersen.h>

#include <cstdlib>
#include <string>
#include <vector>

using namespace llvm;
using namespace std;
using namespace SVF;

int main(int argc, char **argv) {
  std::vector<std::string> modNameVec = OptionBase::parseOptions(
      argc, argv, "bitcodes", "[options] <bitcode...>");

  if (Options::WriteAnder() == "ir_annotator")
    LLVMModuleSet::preProcessBCs(modNameVec);
  LLVMModuleSet::buildSVFModule(modNameVec);

  SVFIR *pag = SVFIRBuilder().build();
  Andersen *ander = AndersenWaveDiff::createAndersenWaveDiff(pag);
  SVFG *svfg = SVFGBuilder().buildFullSVFG(ander);

  svfg->dump("input");

  AndersenWaveDiff::releaseAndersenWaveDiff();
  SVF::LLVMModuleSet::releaseLLVMModuleSet();
  SVFIR::releaseSVFIR();
  llvm::llvm_shutdown();

  return EXIT_SUCCESS;
}

I could then build, install, and run my tool like this:

> cmake -G "Ninja" -B build
> cmake --build build
> cmake --install build --prefix $(pwd)/install
> ./install/bin/foo some/example/bitcode.bc

The find_package(SVF) snippet finds the SVF CMake package. Internally, this package defines its dependencies, but also exposes its dependencies' dependencies (e.g., ABI flags required by the specific LLVM instance used, like -fno-rtti). Consequently, to use SVF, I don't need to also find LLVM and/or Z3 and link against them — CMake resolves that based on SVF's package. This also works for non-system wide installations of SVF (e.g., installed to ~/SVF/install/...) by setting the environment variable $SVF_DIR or passing it as a search path to the find_package(SVF HINTS <path>) invocation.

Of course, since the Release-build tree still exists as it did before, finding & linking against SVF how most test cases and examples do currently is still supported. Moreover, while headers are now cleanly installed into <prefix>/include/SVF/..., using the headers without the prefix (e.g., #include <WPA/Andersen.h>) is still possible (this is also exposed by the CMake package implicitly).

Moreover, after the find_package(SVF) call, its configuration variables become available, so a user can directly use CMake variables like ${SVF_USE_PIC} to check whether SVF was built with/without PIC, ${SVF_BUILD_TYPE} to check the used build type, or install paths (even when the installation prefix is changed after CMake configuration (i.e., during installation with cmake --install <build_dir> --prefix=some/new/install/prefix) like ${SVF_INSTALL_BINDIR} or ${SVF_INSTALL_INCLUDEDIR}.

Moreover, this keeps the dependencies "separate", ensuring only the SvfCore library depends on LLVM. In the future, this could easily be modified to ensure LLVM needn't be found if someone only wants to use & link against SvfCore (but not SvfLLVM).

I hope this clarifies the changes & use-case a bit more! As I said, I'd be happy to update the Wiki page to describe how to build, install, and use SVF and its dependencies like this, as well as write a new Wiki page describing how to use SVF within the default LLVM clang pass-pipeline! Would you still like me to split this pull request (no problem), or should I leave it combined if this makes enough sense to merge as-is?

Johanmyst added 6 commits May 6, 2025 12:32
Numerous fixes and improvements to the overall build system. Core changes include:
  - Moved CMake files (templates, input, etc.) into dedicated `cmake` subdirectory
  - Fixed relocatability of install tree (support for `cmake --install <build-dir> --install-prefix=<prefix>`)
  - Relocatable packages no longer depend on absolute paths of dependencies (i.e., Z3, LLVM)
  - SVF root, source, build, and install directories now exposed properly & correctly handle relocations
  - Required ABI flags of users (e.g., minimum C++ standard, PIC, etc.) now exposed through interface library (invisible)
  - Exported `pkconfig` package defines the core required flags
  - Explicit support for using SVF in-tree (i.e., building SVF from another project's `CMakeLists.txt` with `add_subdirectory(SVF)`)
  - Added IPO compiler support check when LTO is enabled (trying to use LTO if not supported throws a fatal error)
  - Required ABI flags of dependencies (i.e., Z3, LLVM) are now exposed publically on the CMake package's libraries (i.e., `SvfCore` and `SvfLLVM`; so they are inherited when using SVF with `find_package(SVF)` and `target_link_libraries(myLib <PUBLIC|PRIVATE|INTERFACE> SVF::Core SVF::LLVM)`)
  - Exposing SVF version to parent (i.e., updating `SVF_VERSION` defined in parent scope when adding SVF with `add_subdirectory(SVF)`) now conditional on there being a parent project to suppress warnings when not using SVF this way
The current GitHub Workflow coverage test removes coverage information from certain files (i.e., with `lcov --remove coverage.info '/usr/*' --output-file coverage.info`). If the given directory (e.g., `/usr/*`) contains no files with coverage information, trying to remove this coverage information throws an error. The current workflow only adds `--ignore-errors unused` for the files with coverage information in `${{github.workspace}}/svf/include/FastCluster/*`, but not for the other directories. As this information should be removed anyway (also for the other directories), not having coverage information to begin with shouldn't be seen as an error. This commit applies the `--ignore-errors unused` flag consistently to the other `lcov --remove ...` invocations.
While there are formatting description files for C/C++ (i.e., `.clang-format`), no equivalent formatting specification exists for CMake (to automatically format CMake files (e.g., `CMakeLists.txt`) consistently with `cmake-format`). This commit adds such a format description to ensure all CMake files are formatted consistently.
Most `get...()` functions have `has...()` equivalents (e.g., `getDefSVFGNode()` and `hasDefSVFGNode()`). However, certain getters (e.g., `getLLVMValue()`, `getLHSTopLevelPtr()` had no such existence-checking counterparts. This commit adds a number of these checking functions in cases where they were missing.

Note that these are for cases where the getters fail an assertion when no valid value exists. Cases where the getter simply returns a null-pointer on failure to find any corresponding values (e.g., `ICFG::getFunEntryBlock()`, `ICFG::getFunExitBlock()`, etc.), so a null-check can be used to catch cases where the getter fails to get any corresponding object, and thus don't need a corresponding existence checker. To avoid changing the current behaviour of the assertions and getters, I added additional existence-checking functions. This does mean the container will be searched twice (once in the existence-check and then again in the getter itself) and thus wastes performance. To avoid this, all current getters could be modified so that all getters return null-pointers on failures, but I didn't want to make such an intrusive change in this commit.
…istent with other WPA implementations)

Currently, most WPA implementations (e.g., `AndersenWaveDiff`) expose read-only functions (e.g., `printStat()`, `dumpTopLevelPtsTo()`, etc.) publically, as well as "constructors" like `solveAndWritePtsToFile()`, `readPtsFromFile()`, etc. However, `VersionedFlowSensitive` is the only one to mark these functions as `private`/`protected`. This makes it annoying to use polymorphism over the underlying PTA base class (i.e., `BVDataPTAImpl`) and the overridden virtual functions. This commit fixes that by marking these functions as public in the `VersionedFlowSensitive` WPA class.

This change allows me to use polymorphism to conveniently try different WPA backends like this:

```
SVF::BVDataPTAImpl *pta = nullptr;

switch(getChosenBackend()) {
    case Andersen_BASE:
        pta = new Andersen(...);
        break;
    case VFS_WPA:
        pta = new VersionedFlowSensitive(...);
        break;
    default:
        break;
}
```
…existing results

The current `SVFGBuilder` optimises the SVFG based on the command-line argument. However, when using SVF as a library, this is inconvenient. This commit changes the builder to use an additional flag to control optimisation behaviour.

Moreover, this commit fixes the reading/writing issues for optimised SVFGs. Currently, the writing happens post-optimisation, which causes the reading step to fail due to missing/unexpected nodes. This commit also ensures the *unoptimised SVFG* is written to a file *prior to optimising it*, and reads an unoptimised SVFG from a file and then optimises it. This fixes these crashing issues.
@Johanmyst Johanmyst force-pushed the fixes-and-improvements branch from def7c04 to d25a348 Compare May 6, 2025 12:34
@Johanmyst
Copy link
Contributor Author

Rebased from the current upstream's head to make merging easier. @yuleisui, let me know if you'd still like me to separate this pull request or if my previous comment sufficiently clarified my intent/use-case and you want to merge it as-is!

@yuleisui
Copy link
Collaborator

yuleisui commented May 7, 2025

Rebased from the current upstream's head to make merging easier. @yuleisui, let me know if you'd still like me to separate this pull request or if my previous comment sufficiently clarified my intent/use-case and you want to merge it as-is!

Thanks, and could you separate the pull request? I can merge your "non-Build System" one first.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants