From 77b4aff835f97b324cc79097db51bd693d915909 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Thu, 4 Sep 2025 19:02:58 +0200 Subject: [PATCH 01/16] added test coverage for ci --- .github/workflows/ci.yml | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0031d9ef5d..821f67ff39 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,3 +76,20 @@ jobs: cd ./examples/how-to cmake -S . -B build -Dphasar_ROOT=../../INSTALL cmake --build ./build --target run_sample_programs + test-coverage: + runs-on: ${{ matrix.os }} + steps: + - name: Checkout Project + uses: actions/checkout@v4.2.2 + + - name: Build Project + uses: threeal/cmake-action@v2.0.0 + + - name: Test Project + uses: threeal/ctest-action@v1.1.0 + + - name: Check Test Coverage and generate HTML report + uses: threeal/gcovr-action@v1.1.0 + with: + gcov-executable: llvm-cov gcov + html-out: coverage.html From e2add939c55123ff2e56ced9d3ee78a3c553bfd3 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Thu, 4 Sep 2025 19:13:36 +0200 Subject: [PATCH 02/16] merged coverage job into build, removed all but one step for testing --- .github/workflows/ci.yml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 821f67ff39..3ed8eda4e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,17 +76,6 @@ jobs: cd ./examples/how-to cmake -S . -B build -Dphasar_ROOT=../../INSTALL cmake --build ./build --target run_sample_programs - test-coverage: - runs-on: ${{ matrix.os }} - steps: - - name: Checkout Project - uses: actions/checkout@v4.2.2 - - - name: Build Project - uses: threeal/cmake-action@v2.0.0 - - - name: Test Project - uses: threeal/ctest-action@v1.1.0 - name: Check Test Coverage and generate HTML report uses: threeal/gcovr-action@v1.1.0 From 6537bf938cba499c7cca143e9096fdf9fd303e10 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Tue, 14 Oct 2025 00:25:17 +0200 Subject: [PATCH 03/16] test code for meeting --- CMakeLists.txt | 74 ++++++++++++++++++- cmake/phasar_macros.cmake | 11 +++ .../PhasarLLVM/TypeHierarchy/CMakeLists.txt | 1 + 3 files changed, 85 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 58083e3b98..fce4cd2407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE) # https://reviews.llvm.org/D157613 string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe") -string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer") +string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer -fprofile-instr-generate -fcoverage-mapping -fprofile-arcs -ftest-coverage") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer") string(APPEND CMAKE_CXX_FLAGS_RELEASE "") @@ -565,3 +565,75 @@ if(PHASAR_BUILD_DOC) VERBATIM ) endif() + +# Generate code coverage +# TODO: code coverage will automatically be enabled when in debug mode. +# Consider creating a seperate variable for coverage generation. +# IIrc debug mode is needed though for coverage generation, double check this. + +# TODO: code below won't work probably. +# How does nlohmann json execute the test files to get the profraw?! +# -> Apparently coverall does all the work or something? + +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + message("TEST1") + message("build type: ${CMAKE_BUILD_TYPE}") + message("unittests: ${PhasarUnitTests}") + message("TEST2") + add_custom_target(generate_coverage_data + COMMAND ninja + + set(cov_cmd "llvm-profdata-20") + set(cov_mode "merge") + set(cov_arg "-sparse") + set(cov_out "-o \"$filename\".profdata") + execute_process(cov_cmd cov_mode cov_arg "default.profraw" cov_out) + # COMMAND llvm-profdata-20 merge -sparse default.profraw -o "$filename".profdata + + COMMENT "Compile with coverage flags and collect coverage data" + ) + # find_package(Doxygen REQUIRED) + + # set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in) + # set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) + # configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + + # add_custom_target(doc_doxygen ALL + # COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + # COMMENT "Generating API documentation with Doxygen" + # VERBATIM + # ) + + # nlohmann json approach + + +############################################################################### +# Coverage. +############################################################################### + +# add_custom_target(ci_test_coverage +# COMMAND CXX=g++ ${CMAKE_COMMAND} +# -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" +# -DJSON_BuildTests=ON +# -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage +# COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage +# COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure +# +# COMMAND CXX=g++ ${CMAKE_COMMAND} +# -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="-m32;--coverage;-fprofile-arcs;-ftest-coverage" +# -DJSON_BuildTests=ON -DJSON_32bitTest=ONLY +# -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage32 +# COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32 +# COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure +# +# COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused +# COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused +# COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept +# COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept +# +# COMMENT "Compile and test with coverage" +# ) + + +endif() diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 2bd952852c..050251dc3b 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -365,3 +365,14 @@ macro(subdirlist result curdir) set(${result} ${dirlist}) endmacro(subdirlist) + +# TODO: remove this. It makes no sense, since the tests only exist after compiling. +function(run_test_for_prowraw test_name) + message("Run test to generate .prowraw coverage data for test ${test_name}") + + get_filename_component(test ${test_name} NAME_WE) + + message("${CMAKE_CURRENT_SOURCE_DIR}") + execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/./${test_name}") + +endfunction() diff --git a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt index 75f52a91d5..7dd9e1b204 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt +++ b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt @@ -7,4 +7,5 @@ set(PointerSources foreach(TEST_SRC ${PointerSources}) add_phasar_unittest(${TEST_SRC}) + run_test_for_prowraw(${TEST_SRC}) endforeach(TEST_SRC) From 7cc5f63675247e2fa3a0be731edaa539515dfddc Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 8 Sep 2025 15:20:44 +0200 Subject: [PATCH 04/16] Call-Graph Improvements (#785) * minor fix in CHA and RTA resolvers * Several small improvements for call-graph resolving * Fix out-of-bounds access in getVarTypeFromIR() * Add address-taken functions caching in base resolver * Some cleanup in resolvers * pre-commit * Fix bug in the overloads of buildLLVMBasedCallGraph() that takes a CallGraphAnalysisType --- .../ControlFlow/Resolver/CHAResolver.h | 4 - .../ControlFlow/Resolver/NOResolver.h | 4 - .../ControlFlow/Resolver/OTFResolver.h | 23 +--- .../ControlFlow/Resolver/RTAResolver.h | 2 - .../ControlFlow/Resolver/Resolver.h | 65 ++++++---- .../ControlFlow/LLVMBasedCallGraphBuilder.cpp | 9 +- .../ControlFlow/Resolver/CHAResolver.cpp | 5 - .../ControlFlow/Resolver/NOResolver.cpp | 6 - .../ControlFlow/Resolver/OTFResolver.cpp | 113 ++++++++---------- .../ControlFlow/Resolver/Resolver.cpp | 101 +++++++++++++++- 10 files changed, 194 insertions(+), 138 deletions(-) diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h index 8186bdba16..f4b73965cb 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h @@ -20,10 +20,6 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" #include "phasar/Utils/MaybeUniquePtr.h" -namespace llvm { -class CallBase; -} // namespace llvm - namespace psr { class DIBasedTypeHierarchy; diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h index 044815e287..439afd2a72 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h @@ -12,10 +12,6 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" -namespace llvm { -class CallBase; -} // namespace llvm - namespace psr { /// \brief A resolver that doesn't resolve indirect- and virtual calls diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h index 41b3f6e878..ee0265a38d 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h @@ -20,22 +20,8 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" -#include -#include -#include -#include - -namespace llvm { -class CallBase; -class Function; -class Type; -class Value; -} // namespace llvm - namespace psr { -class DIBasedTypeHierarchy; - /// \brief A resolver that uses alias information to resolve indirect and /// virtual calls class OTFResolver : public Resolver { @@ -54,18 +40,11 @@ class OTFResolver : public Resolver { void resolveFunctionPointer(FunctionSetTy &PossibleTargets, const llvm::CallBase *CallSite) override; - static std::set - getReachableTypes(const LLVMAliasInfo::AliasSetTy &Values); - - static std::vector> - getActualFormalPointerPairs(const llvm::CallBase *CallSite, - const llvm::Function *CalleeTarget); - [[nodiscard]] std::string str() const override; [[nodiscard]] bool mutatesHelperAnalysisInformation() const noexcept override { - return true; + return !PT.isInterProcedural(); } protected: diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h index 6e89063a9d..f9165a7d96 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h @@ -22,12 +22,10 @@ #include namespace llvm { -class CallBase; class DICompositeType; } // namespace llvm namespace psr { -class DIBasedTypeHierarchy; /// \brief A resolver that performs Rapid Type Analysis to resolve calls /// to C++ virtual functions. Requires debug information. diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h index 09b8147424..cc1e31100a 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h @@ -20,6 +20,7 @@ #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/DerivedTypes.h" #include @@ -44,6 +45,10 @@ enum class CallGraphAnalysisType; [[nodiscard]] std::optional getVFTIndex(const llvm::CallBase *CallSite); +/// Similar to getVFTIndex(), but also returns a pointer to the vtable +[[nodiscard]] std::optional> +getVFTIndexAndVT(const llvm::CallBase *CallSite); + /// Assuming that `CallSite` is a call to a non-static member function, /// retrieves the type of the receiver. Returns nullptr, if the receiver-type /// could not be extracted @@ -67,25 +72,16 @@ getReceiverType(const llvm::CallBase *CallSite); [[nodiscard]] bool isVirtualCall(const llvm::Instruction *Inst, const LLVMVFTableProvider &VTP); +/// A variant of F->hasAddressTaken() that is better suited for our use cases. +/// +/// Especially, it filteres out global aliases. +[[nodiscard]] bool isAddressTakenFunction(const llvm::Function *F); + /// \brief A base class for call-target resolvers. Used to build call graphs. /// /// Create a specific resolver by making a new class, inheriting this resolver /// class and implementing the virtual functions as needed. class Resolver { -protected: - const LLVMProjectIRDB *IRDB; - const LLVMVFTableProvider *VTP; - - const llvm::Function * - getNonPureVirtualVFTEntry(const llvm::DIType *T, unsigned Idx, - const llvm::CallBase *CallSite, - const llvm::DIType *ReceiverType) { - if (!VTP) { - return nullptr; - } - return psr::getNonPureVirtualVFTEntry(T, Idx, CallSite, *VTP, ReceiverType); - } - public: using FunctionSetTy = llvm::SmallDenseSet; @@ -93,17 +89,23 @@ class Resolver { virtual ~Resolver() = default; - virtual void preCall(const llvm::Instruction *Inst); + [[deprecated("With the removal of DTAResolver, this is not used " + "anymore")]] virtual void + preCall(const llvm::Instruction *Inst); virtual void handlePossibleTargets(const llvm::CallBase *CallSite, FunctionSetTy &PossibleTargets); - virtual void postCall(const llvm::Instruction *Inst); + [[deprecated("With the removal of DTAResolver, this is not used " + "anymore")]] virtual void + postCall(const llvm::Instruction *Inst); [[nodiscard]] FunctionSetTy resolveIndirectCall(const llvm::CallBase *CallSite); - virtual void otherInst(const llvm::Instruction *Inst); + [[deprecated("With the removal of DTAResolver, this is not used " + "anymore")]] virtual void + otherInst(const llvm::Instruction *Inst); [[nodiscard]] virtual std::string str() const = 0; @@ -115,11 +117,30 @@ class Resolver { // Conservatively returns true. Override if possible return true; } - static std::unique_ptr create(CallGraphAnalysisType Ty, - const LLVMProjectIRDB *IRDB, - const LLVMVFTableProvider *VTP, - const DIBasedTypeHierarchy *TH, - LLVMAliasInfoRef PT = nullptr); + + [[nodiscard]] llvm::ArrayRef + getAddressTakenFunctions(); + + [[nodiscard]] static std::unique_ptr + create(CallGraphAnalysisType Ty, const LLVMProjectIRDB *IRDB, + const LLVMVFTableProvider *VTP, const DIBasedTypeHierarchy *TH, + LLVMAliasInfoRef PT = nullptr); + +protected: + const llvm::Function * + getNonPureVirtualVFTEntry(const llvm::DIType *T, unsigned Idx, + const llvm::CallBase *CallSite, + const llvm::DIType *ReceiverType) { + if (!VTP) { + return nullptr; + } + return psr::getNonPureVirtualVFTEntry(T, Idx, CallSite, *VTP, ReceiverType); + } + + const LLVMProjectIRDB *IRDB{}; + const LLVMVFTableProvider *VTP{}; + std::optional> + AddressTakenFunctions{}; protected: virtual void resolveVirtualCall(FunctionSetTy &PossibleTargets, diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp index e3f65c00ec..492176a510 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp @@ -153,13 +153,9 @@ bool Builder::processFunction(const llvm::Function *F) { for (const auto &I : llvm::instructions(F)) { const auto *CS = llvm::dyn_cast(&I); if (!CS) { - Res->otherInst(&I); continue; } - Res->preCall(&I); - scope_exit PostCall = [&] { Res->postCall(&I); }; - FixpointReached &= fillPossibleTargets(PossibleTargets, *Res, CS, IndirectCalls); @@ -203,9 +199,6 @@ bool Builder::constructDynamicCall(const llvm::Instruction *CS) { "Looking into dynamic call-site: "); PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", " " << llvmIRToString(CS)); - Res->preCall(CallSite); - scope_exit PostCall = [&] { Res->postCall(CallSite); }; - // call the resolve routine auto PossibleTargets = Res->resolveIndirectCall(CallSite); @@ -275,7 +268,7 @@ auto psr::buildLLVMBasedCallGraph( PT = PTOwn.asRef(); } - auto Res = Resolver::create(CGType, &IRDB, &VTP, &TH); + auto Res = Resolver::create(CGType, &IRDB, &VTP, &TH, PT); return buildLLVMBasedCallGraph(IRDB, *Res, EntryPoints, S); } diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp index 393e810fb1..56b9565a80 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/CHAResolver.cpp @@ -21,15 +21,10 @@ #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" -#include "llvm/IR/Constants.h" -#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Module.h" #include -using namespace std; using namespace psr; CHAResolver::CHAResolver(const LLVMProjectIRDB *IRDB, diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp index 9dc6a56c28..7b962135eb 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp @@ -16,12 +16,8 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h" -#include - using namespace psr; -namespace psr { - NOResolver::NOResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP) : Resolver(IRDB, VTP) {} @@ -33,5 +29,3 @@ void NOResolver::resolveFunctionPointer(FunctionSetTy & /*PossibleTargets*/, const llvm::CallBase * /*CallSite*/) {} std::string NOResolver::str() const { return "NOResolver"; } - -} // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp index c4168649c9..5adf72192c 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp @@ -22,7 +22,6 @@ #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstIterator.h" -#include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Support/Casting.h" @@ -34,6 +33,55 @@ OTFResolver::OTFResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, LLVMAliasInfoRef PT) : Resolver(IRDB, VTP), PT(PT) {} +static std::vector> +getActualFormalPointerPairs(const llvm::CallBase *CallSite, + const llvm::Function *CalleeTarget) { + std::vector> Pairs; + Pairs.reserve(CallSite->arg_size()); + // ordinary case + + unsigned Idx = 0; + for (; Idx < CallSite->arg_size() && Idx < CalleeTarget->arg_size(); ++Idx) { + // only collect pointer typed pairs + if (CallSite->getArgOperand(Idx)->getType()->isPointerTy() && + CalleeTarget->getArg(Idx)->getType()->isPointerTy()) { + Pairs.emplace_back(CallSite->getArgOperand(Idx), + CalleeTarget->getArg(Idx)); + } + } + + if (CalleeTarget->isVarArg()) { + // in case of vararg, we can pair-up incoming pointer parameters with the + // vararg pack of the callee target. the vararg pack will alias + // (intra-procedurally) with any pointer values loaded from the pack + const llvm::AllocaInst *VarArgs = nullptr; + + for (const auto &I : llvm::instructions(CalleeTarget)) { + if (const auto *Alloca = llvm::dyn_cast(&I)) { + if (const auto *AT = + llvm::dyn_cast(Alloca->getAllocatedType())) { + if (const auto *ST = + llvm::dyn_cast(AT->getArrayElementType())) { + if (ST->hasName() && ST->getName() == "struct.__va_list_tag") { + VarArgs = Alloca; + break; + } + } + } + } + } + + if (VarArgs) { + for (; Idx < CallSite->arg_size(); ++Idx) { + if (CallSite->getArgOperand(Idx)->getType()->isPointerTy()) { + Pairs.emplace_back(CallSite->getArgOperand(Idx), VarArgs); + } + } + } + } + return Pairs; +} + void OTFResolver::handlePossibleTargets(const llvm::CallBase *CallSite, FunctionSetTy &CalleeTargets) { // if we have no inter-procedural points-to information, use call-graph @@ -71,7 +119,7 @@ void OTFResolver::resolveVirtualCall(FunctionSetTy &PossibleTargets, PHASAR_LOG_LEVEL(DEBUG, "Call virtual function: " << llvmIRToString(CallSite)); - auto RetrievedVtableIndex = getVFTIndex(CallSite); + auto RetrievedVtableIndex = getVFTIndexAndVT(CallSite); if (!RetrievedVtableIndex.has_value()) { // An error occured PHASAR_LOG_LEVEL(DEBUG, @@ -81,11 +129,12 @@ void OTFResolver::resolveVirtualCall(FunctionSetTy &PossibleTargets, return; } - auto VtableIndex = RetrievedVtableIndex.value(); + auto [VtablePtr, VtableIndex] = RetrievedVtableIndex.value(); PHASAR_LOG_LEVEL(DEBUG, "Virtual function table entry is: " << VtableIndex); - auto PTS = PT.getAliasSet(CallSite->getCalledOperand(), CallSite); + auto PTS = PT.getAliasSet(VtablePtr, CallSite); + for (const auto *P : *PTS) { if (const auto *PGV = llvm::dyn_cast(P)) { if (PGV->hasName() && @@ -201,60 +250,4 @@ void OTFResolver::resolveFunctionPointer(FunctionSetTy &PossibleTargets, } } -std::set -OTFResolver::getReachableTypes(const LLVMAliasInfo::AliasSetTy &Values) { - std::set Types; - // an allocation site can either be an AllocaInst or a call to an - // allocating function - for (const auto *V : Values) { - if (const auto *Alloc = llvm::dyn_cast(V)) { - Types.insert(Alloc->getAllocatedType()); - } else { - // usually if an allocating function is called, it is immediately - // bit-casted - // to the desired allocated value and hence we can determine it from - // the destination type of that cast instruction. - for (const auto *User : V->users()) { - if (const auto *Cast = llvm::dyn_cast(User)) { - Types.insert(Cast->getDestTy()); - } - } - } - } - return Types; -} - -std::vector> -OTFResolver::getActualFormalPointerPairs(const llvm::CallBase *CallSite, - const llvm::Function *CalleeTarget) { - std::vector> Pairs; - Pairs.reserve(CallSite->arg_size()); - // ordinary case - - unsigned Idx = 0; - for (; Idx < CallSite->arg_size() && Idx < CalleeTarget->arg_size(); ++Idx) { - // only collect pointer typed pairs - if (CallSite->getArgOperand(Idx)->getType()->isPointerTy() && - CalleeTarget->getArg(Idx)->getType()->isPointerTy()) { - Pairs.emplace_back(CallSite->getArgOperand(Idx), - CalleeTarget->getArg(Idx)); - } - } - - if (CalleeTarget->isVarArg()) { - // in case of vararg, we can pair-up incoming pointer parameters with the - // vararg pack of the callee target. the vararg pack will alias - // (intra-procedurally) with any pointer values loaded from the pack - - if (const auto *VarArgs = getVaListTagOrNull(*CalleeTarget)) { - for (; Idx < CallSite->arg_size(); ++Idx) { - if (CallSite->getArgOperand(Idx)->getType()->isPointerTy()) { - Pairs.emplace_back(CallSite->getArgOperand(Idx), VarArgs); - } - } - } - } - return Pairs; -} - std::string OTFResolver::str() const { return "OTF"; } diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp index f97fc8c1f1..a1d8bd5b1c 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp @@ -35,6 +35,7 @@ #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" @@ -42,6 +43,8 @@ #include #include +using namespace psr; + std::optional psr::getVFTIndex(const llvm::CallBase *CallSite) { // deal with a virtual member function // retrieve the vtable entry that is called @@ -61,6 +64,29 @@ std::optional psr::getVFTIndex(const llvm::CallBase *CallSite) { return std::nullopt; } +std::optional> +psr::getVFTIndexAndVT(const llvm::CallBase *CallSite) { + // deal with a virtual member function + // retrieve the vtable entry that is called + const auto *Load = + llvm::dyn_cast(CallSite->getCalledOperand()); + if (Load == nullptr) { + return std::nullopt; + } + + const auto *GEP = + llvm::dyn_cast(Load->getPointerOperand()); + if (GEP == nullptr) { + return std::nullopt; + } + + if (auto *CI = llvm::dyn_cast(GEP->getOperand(1))) { + return {{GEP->getPointerOperand(), CI->getZExtValue()}}; + } + + return std::nullopt; +} + static const llvm::DIType *stripPointerTypes(const llvm::DIType *DITy) { while (const auto *DerivedTy = llvm::dyn_cast_if_present(DITy)) { @@ -155,7 +181,59 @@ bool psr::isVirtualCall(const llvm::Instruction *Inst, return getVFTIndex(CallSite) >= 0; } -namespace psr { +// Derived from LLVM's llvm::Function::hasAddressTaken() +static bool isAddressTakenImpl(const llvm::Value *F) { + if (!F) { + return false; + } + + for (const auto &Use : F->uses()) { + const auto *User = Use.getUser(); + + if (llvm::isa(User)) { + if (isAddressTakenImpl(User)) { + return true; + } + + continue; + } + + if (const auto *Glob = llvm::dyn_cast(User)) { + if (Glob->getName() == "llvm.compiler.used" || + Glob->getName() == "llvm.used") { + continue; + } + + return true; + } + + const auto *Call = llvm::dyn_cast(User); + if (!Call) { + return true; + } + + if (Call->isDebugOrPseudoInst()) { + continue; + } + + const auto *Intrinsic = llvm::dyn_cast(Call); + if (Intrinsic && Intrinsic->isAssumeLikeIntrinsic()) { + continue; + } + + if (Call->isCallee(&Use)) { + continue; + } + + return true; + } + + return false; +} + +bool psr::isAddressTakenFunction(const llvm::Function *F) { + return isAddressTakenImpl(F); +} Resolver::Resolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP) : IRDB(IRDB), VTP(VTP) { @@ -183,6 +261,21 @@ auto Resolver::resolveIndirectCall(const llvm::CallBase *CallSite) return PossibleTargets; } +llvm::ArrayRef Resolver::getAddressTakenFunctions() { + if (!AddressTakenFunctions) { + auto &ATF = AddressTakenFunctions.emplace(); + // XXX: Find better heuristic + ATF.reserve(IRDB->getNumFunctions() / 2); + for (const auto *F : IRDB->getAllFunctions()) { + if (isAddressTakenFunction(F)) { + ATF.push_back(F); + } + } + } + + return *AddressTakenFunctions; +} + void Resolver::resolveFunctionPointer(FunctionSetTy &PossibleTargets, const llvm::CallBase *CallSite) { // we may wish to optimise this function @@ -191,8 +284,8 @@ void Resolver::resolveFunctionPointer(FunctionSetTy &PossibleTargets, PHASAR_LOG_LEVEL(DEBUG, "Call function pointer: " << llvmIRToString(CallSite)); - for (const auto *F : IRDB->getAllFunctions()) { - if (F->hasAddressTaken() && isConsistentCall(CallSite, F)) { + for (const auto *F : getAddressTakenFunctions()) { + if (isConsistentCall(CallSite, F)) { PossibleTargets.insert(F); } } @@ -230,5 +323,3 @@ std::unique_ptr Resolver::create(CallGraphAnalysisType Ty, llvm_unreachable("All possible callgraph algorithms should be handled in the " "above switch"); } - -} // namespace psr From 3496cf6522ef88286fd4f3dddac9899f91512132 Mon Sep 17 00:00:00 2001 From: Maximilian Leo Huber Date: Mon, 8 Sep 2025 13:48:26 +0000 Subject: [PATCH 05/16] Dbg-Info based Type Matching (#791) * moved stripPointerTypes() + debug prints * MetadataKind approach * I think we don't get around using getName() * unittests still fail * bugfix + cleanup * Some cleanup * Implement allocated-types collection in terms of debug info * Fix libdeps * Remove some unneeded includes --------- Co-authored-by: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Co-authored-by: Fabian Schiebel --- .../IfdsIde/Problems/IDETypeStateAnalysis.h | 14 ++-- .../CSTDFILEIOTypeStateDescription.h | 1 - .../OpenSSLEVPKDFDescription.h | 1 - .../TypeStateDescription.h | 6 +- .../Passes/GeneralStatisticsAnalysis.h | 4 +- .../phasar/PhasarLLVM/Utils/AllocatedTypes.h | 23 +++++++ .../phasar/PhasarLLVM/Utils/LLVMShorthands.h | 10 +++ .../ControlFlow/Resolver/RTAResolver.cpp | 51 +------------- .../ControlFlow/Resolver/Resolver.cpp | 9 --- .../IfdsIde/Problems/IDETypeStateAnalysis.cpp | 69 ++++--------------- .../CSTDFILEIOTypeStateDescription.cpp | 4 +- .../OpenSSLEVPKDFCTXDescription.cpp | 4 +- .../OpenSSLEVPKDFDescription.cpp | 4 +- .../OpenSSLSecureHeapDescription.cpp | 2 +- .../OpenSSLSecureMemoryDescription.cpp | 2 +- .../Passes/GeneralStatisticsAnalysis.cpp | 35 ++-------- lib/PhasarLLVM/Utils/AllocatedTypes.cpp | 62 +++++++++++++++++ lib/PhasarLLVM/Utils/LLVMShorthands.cpp | 37 ++++++++++ 18 files changed, 170 insertions(+), 168 deletions(-) create mode 100644 include/phasar/PhasarLLVM/Utils/AllocatedTypes.h create mode 100644 lib/PhasarLLVM/Utils/AllocatedTypes.cpp diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index 0b68a4f7ac..a09c1a63b1 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -15,6 +15,7 @@ #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" @@ -35,9 +36,6 @@ namespace psr { -class LLVMBasedICFG; -class LLVMTypeHierarchy; - namespace detail { class IDETypeStateAnalysisBaseCommon : public LLVMAnalysisDomainDefault { @@ -123,7 +121,7 @@ class IDETypeStateAnalysisBase container_type getLocalAliasesAndAllocas(d_t V, llvm::StringRef Fname); /** - * @brief Checks if the type machtes the type of interest. + * @brief Checks if the type matches the type of interest. */ bool hasMatchingType(d_t V); @@ -132,7 +130,7 @@ class IDETypeStateAnalysisBase return generateFlow(FactToGenerate, LLVMZeroValue::getInstance()); } - bool hasMatchingTypeName(const llvm::Type *Ty); + bool hasMatchingTypeName(const llvm::DIType *DITy); std::map AliasCache; LLVMAliasInfoRef PT{}; @@ -283,11 +281,7 @@ class IDETypeStateAnalysis template >> TSConstant(l_t Value, EmptyType /*unused*/ = {}) noexcept - : ConstantEdgeFunction{Value} { - if constexpr (!HasJoinLatticeTraits) { - this->TSD = TSD; - } - } + : ConstantEdgeFunction{Value} {} /// XXX: Cannot default compose() and join(), because l_t does not implement /// JoinLatticeTraits (because bottom value is not constant) diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h index 9c4da46d74..d5a9b4f869 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h @@ -15,7 +15,6 @@ #include "llvm/Support/raw_ostream.h" -#include #include #include diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h index 324333c623..27eed099ea 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h @@ -12,7 +12,6 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" -#include #include #include diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h index 0e6865686b..169b2b8f1e 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h @@ -12,11 +12,13 @@ #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" -#include "llvm/IR/InstrTypes.h" - #include #include +namespace llvm { +class CallBase; +} // namespace llvm + namespace psr { struct TypeStateDescriptionBase { diff --git a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h index fddf1e98d5..fce1a4103b 100644 --- a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h +++ b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h @@ -20,6 +20,7 @@ #include "llvm/IR/PassManager.h" #include +#include namespace llvm { class Type; @@ -27,6 +28,7 @@ class Value; class Instruction; class AnalysisUsage; class Module; +class DICompositeType; } // namespace llvm namespace psr { @@ -67,7 +69,7 @@ struct GeneralStatistics { size_t NumInstWithMultipleUses = 0; size_t NumInstsUsedOutsideBB = 0; size_t NonVoidInsts = 0; - std::set AllocatedTypes; + std::vector AllocatedTypes; std::set AllocaInstructions; std::set RetResInstructions; std::string ModuleName{}; diff --git a/include/phasar/PhasarLLVM/Utils/AllocatedTypes.h b/include/phasar/PhasarLLVM/Utils/AllocatedTypes.h new file mode 100644 index 0000000000..eeba5632ee --- /dev/null +++ b/include/phasar/PhasarLLVM/Utils/AllocatedTypes.h @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (c) 2025 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_UTILS_ALLOCATEDTYPES_H +#define PHASAR_PHASARLLVM_UTILS_ALLOCATEDTYPES_H + +#include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/IR/Module.h" + +#include + +namespace psr { +[[nodiscard]] std::vector +collectAllocatedTypes(const llvm::Module &Mod); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_UTILS_ALLOCATEDTYPES_H diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index f4fad2d4cb..582fef9500 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -104,6 +104,14 @@ std::string llvmIRToShortString(const llvm::Value *V); [[nodiscard]] std::string llvmTypeToString(const llvm::Type *Ty, bool Shorten = false); +/** + * @brief Returns a string-representation of a LLVM Debug-Info type. + * + * @param Shorten Tries to shorten the output + */ +[[nodiscard]] std::string llvmTypeToString(const llvm::DIType *Ty, + bool Shorten = false); + LLVM_DUMP_METHOD void dumpIRValue(const llvm::Value *V); LLVM_DUMP_METHOD void dumpIRValue(const llvm::Instruction *V); LLVM_DUMP_METHOD void dumpIRValue(const llvm::Function *V); @@ -283,6 +291,8 @@ class ModulesToSlotTracker { getVaListTagOrNull(const llvm::Function &Fun); [[nodiscard]] bool isVaListAlloca(const llvm::AllocaInst &Alloc); + +[[nodiscard]] const llvm::DIType *stripPointerTypes(const llvm::DIType *DITy); } // namespace psr #endif diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/RTAResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/RTAResolver.cpp index 33afea40c2..8924999e22 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/RTAResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/RTAResolver.cpp @@ -18,17 +18,12 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" -#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/AllocatedTypes.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" -#include "llvm/BinaryFormat/Dwarf.h" -#include "llvm/IR/DebugInfoMetadata.h" -#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" -#include "llvm/IR/Instructions.h" -#include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" using namespace psr; @@ -85,52 +80,10 @@ void RTAResolver::resolveVirtualCall(FunctionSetTy &PossibleTargets, std::string RTAResolver::str() const { return "RTA"; } -static const llvm::DICompositeType * -isCompositeStructType(const llvm::DIType *Ty) { - if (const auto *CompTy = llvm::dyn_cast_if_present(Ty); - CompTy && (CompTy->getTag() == llvm::dwarf::DW_TAG_structure_type || - CompTy->getTag() == llvm::dwarf::DW_TAG_class_type)) { - - return CompTy; - } - - return nullptr; -} - void RTAResolver::resolveAllocatedCompositeTypes() { if (!AllocatedCompositeTypes.empty()) { return; } - llvm::DenseSet AllocatedTypes; - - for (const auto *Inst : IRDB->getAllInstructions()) { - if (const auto *Alloca = llvm::dyn_cast(Inst)) { - if (const auto *Ty = isCompositeStructType(getVarTypeFromIR(Alloca))) { - AllocatedTypes.insert(Ty); - } - } else if (const auto *Call = llvm::dyn_cast(Inst)) { - if (const auto *Callee = llvm::dyn_cast( - Call->getCalledOperand()->stripPointerCastsAndAliases())) { - if (psr::isHeapAllocatingFunction(Callee)) { - const auto *MDNode = Call->getMetadata("heapallocsite"); - if (const auto *CompTy = llvm:: -#if LLVM_VERSION_MAJOR >= 15 - dyn_cast_if_present -#else - dyn_cast_or_null -#endif - (MDNode); - isCompositeStructType(CompTy)) { - - AllocatedTypes.insert(CompTy); - } - } - } - } - } - - AllocatedCompositeTypes.reserve(AllocatedTypes.size()); - AllocatedCompositeTypes.insert(AllocatedCompositeTypes.end(), - AllocatedTypes.begin(), AllocatedTypes.end()); + AllocatedCompositeTypes = collectAllocatedTypes(*IRDB->getModule()); } diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp index a1d8bd5b1c..b80fe475eb 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp @@ -87,15 +87,6 @@ psr::getVFTIndexAndVT(const llvm::CallBase *CallSite) { return std::nullopt; } -static const llvm::DIType *stripPointerTypes(const llvm::DIType *DITy) { - while (const auto *DerivedTy = - llvm::dyn_cast_if_present(DITy)) { - // get rid of the pointer - DITy = DerivedTy->getBaseType(); - } - return DITy; -} - const llvm::DIType *psr::getReceiverType(const llvm::CallBase *CallSite) { if (!CallSite || CallSite->arg_empty() || (CallSite->hasStructRetAttr() && CallSite->arg_size() < 2)) { diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.cpp index 2ad21cc686..03e287e869 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.cpp @@ -9,10 +9,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h" -#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" -#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" @@ -21,17 +18,17 @@ #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" -#include "llvm/ADT/DenseMap.h" #include "llvm/Demangle/Demangle.h" -#include "llvm/IR/AbstractCallSite.h" +#include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Metadata.h" #include "llvm/IR/Value.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" -#include #include namespace psr::detail { @@ -284,59 +281,23 @@ auto IDETypeStateAnalysisBase::getLocalAliasesAndAllocas( return AliasAndAllocas; } -bool IDETypeStateAnalysisBase::hasMatchingTypeName(const llvm::Type *Ty) { - if (const auto *StructTy = llvm::dyn_cast(Ty); - StructTy && StructTy->hasName()) { - return isTypeNameOfInterest(StructTy->getName()); +bool IDETypeStateAnalysisBase::hasMatchingTypeName(const llvm::DIType *DITy) { + if (llvm::isa(DITy) && !DITy->getName().empty()) { + return isTypeNameOfInterest(DITy->getName()); } - // primitive type - std::string Str; - llvm::raw_string_ostream S(Str); - S << *Ty; - S.flush(); - return isTypeNameOfInterest(Str); + + return true; // Conservatively return true } bool IDETypeStateAnalysisBase::hasMatchingType(d_t V) { - // General case - if (V->getType()->isPointerTy() && !V->getType()->isOpaquePointerTy()) { - if (hasMatchingTypeName(V->getType()->getNonOpaquePointerElementType())) { - return true; - } - // fallthrough - } - if (const auto *Alloca = llvm::dyn_cast(V)) { - if (Alloca->getAllocatedType()->isPointerTy()) { - if (Alloca->getAllocatedType()->isOpaquePointerTy() || - hasMatchingTypeName( - Alloca->getAllocatedType()->getNonOpaquePointerElementType())) { - return true; - } - } - return false; - } - if (const auto *Load = llvm::dyn_cast(V)) { - if (Load->getType()->isPointerTy()) { - if (Load->getType()->isOpaquePointerTy() || - hasMatchingTypeName( - Load->getType()->getNonOpaquePointerElementType())) { - return true; - } + if (const auto *VarTy = getVarTypeFromIR(V)) { + if (const auto *BaseTy = stripPointerTypes(VarTy)) { + return hasMatchingTypeName(BaseTy); } - return false; - } - if (const auto *Store = llvm::dyn_cast(V)) { - if (Store->getValueOperand()->getType()->isPointerTy()) { - if (Store->getValueOperand()->getType()->isOpaquePointerTy() || - hasMatchingTypeName(Store->getValueOperand() - ->getType() - ->getNonOpaquePointerElementType())) { - return true; - } - } - return false; + + return isTypeNameOfInterest(VarTy->getName()); } - return false; -} + return true; +} } // namespace psr::detail diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp index 018ac4d93b..c00c1f2cbe 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.cpp @@ -9,8 +9,6 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/CSTDFILEIOTypeStateDescription.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" - #include "llvm/ADT/StringMap.h" #include "llvm/Support/ErrorHandling.h" @@ -127,7 +125,7 @@ CSTDFILEIOTypeStateDescription::getNextState(llvm::StringRef Tok, } std::string CSTDFILEIOTypeStateDescription::getTypeNameOfInterest() const { - return "struct._IO_FILE"; + return "_IO_FILE"; } std::set diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp index 38a4e22af1..c46ec6d775 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFCTXDescription.cpp @@ -11,8 +11,6 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h" -#include "llvm/IR/Instruction.h" -#include "llvm/IR/Value.h" #include "llvm/Support/ErrorHandling.h" #include @@ -125,7 +123,7 @@ OpenSSLEVPKDFCTXState OpenSSLEVPKDFCTXDescription::getNextState( } std::string OpenSSLEVPKDFCTXDescription::getTypeNameOfInterest() const { - return "struct.evp_kdf_ctx_st"; + return "evp_kdf_ctx_st"; } std::set diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp index d881af8ad6..beeebd9b81 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.cpp @@ -9,8 +9,6 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLEVPKDFDescription.h" -#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/TypeStateDescription.h" - #include "llvm/Support/ErrorHandling.h" #include @@ -74,7 +72,7 @@ OpenSSLEVPKDFDescription::getNextState(llvm::StringRef Tok, } std::string OpenSSLEVPKDFDescription::getTypeNameOfInterest() const { - return "struct.evp_kdf_st"; + return "evp_kdf_st"; } std::set diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp index 716f682a4f..fcdcb70324 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureHeapDescription.cpp @@ -107,7 +107,7 @@ OpenSSLSecureHeapState OpenSSLSecureHeapDescription::getNextState( } std::string OpenSSLSecureHeapDescription::getTypeNameOfInterest() const { - return "i8"; + return {}; } set diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp index a67f17e42b..f836b5d0e2 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/TypeStateDescriptions/OpenSSLSecureMemoryDescription.cpp @@ -110,7 +110,7 @@ OpenSSLSecureMemoryState OpenSSLSecureMemoryDescription::getNextState( } std::string OpenSSLSecureMemoryDescription::getTypeNameOfInterest() const { - return "i8"; // NOT SURE WHAT TO DO WITH THIS + return {}; } set diff --git a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp index 4575fa59ea..9b5befda55 100644 --- a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp +++ b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp @@ -9,6 +9,7 @@ #include "phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h" +#include "phasar/PhasarLLVM/Utils/AllocatedTypes.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/NlohmannLogging.h" @@ -41,31 +42,6 @@ static bool isAddressTaken(const llvm::Function &Fun) noexcept { return false; } -template -static void collectAllocatedTypes(const llvm::CallBase *CallSite, Set &Into) { - for (const auto *User : CallSite->users()) { - if (const auto *Cast = llvm::dyn_cast(User); - Cast && Cast->getDestTy()->isPointerTy() && - !Cast->getDestTy()->isOpaquePointerTy()) { - const auto *ElemTy = Cast->getDestTy()->getNonOpaquePointerElementType(); - if (ElemTy->isStructTy()) { - // finally check for ctor call - for (const auto *User : Cast->users()) { - if (llvm::isa(User)) { - // potential call to the structures ctor - const auto *CTor = llvm::cast(User); - if (CTor->getCalledFunction() && - CTor->getCalledFunction()->getArg(0)->getType() == - Cast->getDestTy()) { - Into.insert(ElemTy); - } - } - } - } - } - } -} - llvm::AnalysisKey GeneralStatisticsAnalysis::Key; // NOLINT GeneralStatistics GeneralStatisticsAnalysis::runOnModule(llvm::Module &M) { PHASAR_LOG_LEVEL(INFO, "Running GeneralStatisticsAnalysis"); @@ -131,7 +107,6 @@ GeneralStatistics GeneralStatisticsAnalysis::runOnModule(llvm::Module &M) { // check for alloca instruction for possible types if (const llvm::AllocaInst *Alloc = llvm::dyn_cast(&I)) { - Stats.AllocatedTypes.insert(Alloc->getAllocatedType()); // do not add allocas from llvm internal functions Stats.AllocaInstructions.insert(&I); ++Stats.AllocationSites; @@ -186,9 +161,6 @@ GeneralStatistics GeneralStatisticsAnalysis::runOnModule(llvm::Module &M) { // do not add allocas from llvm internal functions Stats.AllocaInstructions.insert(&I); ++Stats.AllocationSites; - // check if an instance of a user-defined type is allocated on the - // heap - collectAllocatedTypes(CallSite, Stats.AllocatedTypes); } } else { ++Stats.IndCalls; @@ -197,6 +169,9 @@ GeneralStatistics GeneralStatisticsAnalysis::runOnModule(llvm::Module &M) { } } } + + Stats.AllocatedTypes = collectAllocatedTypes(M); + // check for global pointers for (const auto &Global : M.globals()) { ++Stats.Globals; @@ -323,7 +298,7 @@ template struct AlignNum { } }; template AlignNum(llvm::StringRef, T) -> AlignNum; -AlignNum(llvm::StringRef, size_t, size_t)->AlignNum; +AlignNum(llvm::StringRef, size_t, size_t) -> AlignNum; } // namespace llvm::raw_ostream &psr::operator<<(llvm::raw_ostream &OS, diff --git a/lib/PhasarLLVM/Utils/AllocatedTypes.cpp b/lib/PhasarLLVM/Utils/AllocatedTypes.cpp new file mode 100644 index 0000000000..f8e4f03ff3 --- /dev/null +++ b/lib/PhasarLLVM/Utils/AllocatedTypes.cpp @@ -0,0 +1,62 @@ +#include "phasar/PhasarLLVM/Utils/AllocatedTypes.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Utils/LLVMIRToSrc.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" + +#include "llvm/ADT/DenseSet.h" +#include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" + +static const llvm::DICompositeType * +isCompositeStructType(const llvm::DIType *Ty) { + if (const auto *CompTy = llvm::dyn_cast_if_present(Ty); + CompTy && (CompTy->getTag() == llvm::dwarf::DW_TAG_structure_type || + CompTy->getTag() == llvm::dwarf::DW_TAG_class_type)) { + + return CompTy; + } + + return nullptr; +} + +std::vector +psr::collectAllocatedTypes(const llvm::Module &Mod) { + llvm::DenseSet AllocatedTypes; + + for (const auto &Fun : Mod) { + for (const auto &Inst : llvm::instructions(Fun)) { + if (const auto *Alloca = llvm::dyn_cast(&Inst)) { + if (const auto *Ty = isCompositeStructType(getVarTypeFromIR(Alloca))) { + AllocatedTypes.insert(Ty); + } + } else if (const auto *Call = llvm::dyn_cast(&Inst)) { + if (const auto *Callee = llvm::dyn_cast( + Call->getCalledOperand()->stripPointerCastsAndAliases())) { + if (psr::isHeapAllocatingFunction(Callee)) { + const auto *MDNode = Call->getMetadata("heapallocsite"); + if (const auto *CompTy = llvm:: +#if LLVM_VERSION_MAJOR >= 15 + dyn_cast_if_present +#else + dyn_cast_or_null +#endif + (MDNode); + isCompositeStructType(CompTy)) { + + AllocatedTypes.insert(CompTy); + } + } + } + } + } + } + + std::vector AllocatedCompositeTypes; + AllocatedCompositeTypes.reserve(AllocatedTypes.size()); + AllocatedCompositeTypes.insert(AllocatedCompositeTypes.end(), + AllocatedTypes.begin(), AllocatedTypes.end()); + return AllocatedCompositeTypes; +} diff --git a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp index 18cb20c97e..eb991a8ffa 100644 --- a/lib/PhasarLLVM/Utils/LLVMShorthands.cpp +++ b/lib/PhasarLLVM/Utils/LLVMShorthands.cpp @@ -235,6 +235,34 @@ std::string psr::llvmTypeToString(const llvm::Type *Ty, bool Shorten) { return IRBuffer; } +std::string psr::llvmTypeToString(const llvm::DIType *Ty, bool Shorten) { + if (!Ty) { + return ""; + } + + std::string Ret; + + if (Shorten) { + Ret = Ty->getName().str(); + if (!Ret.empty()) { + // Try to get a fully-qualified name + + const auto *Scope = Ty->getScope(); + while (llvm::isa_and_nonnull(Scope)) { + // XXX: Optimize this + Ret = Scope->getName().str().append("::").append(Ret); + Scope = Scope->getScope(); + } + return Ret; + } + } + + llvm::raw_string_ostream RSO(Ret); + Ty->print(RSO); + return Ret; +} + void psr::dumpIRValue(const llvm::Value *V) { llvm::outs() << llvmIRToString(V) << '\n'; } @@ -617,3 +645,12 @@ bool psr::isVaListAlloca(const llvm::AllocaInst &Alloc) { return false; } + +const llvm::DIType *psr::stripPointerTypes(const llvm::DIType *DITy) { + while (const auto *DerivedTy = + llvm::dyn_cast_if_present(DITy)) { + // get rid of the pointer + DITy = DerivedTy->getBaseType(); + } + return DITy; +} From d41d02da9601de272695c7dd6e92bd818387e598 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 8 Sep 2025 16:06:07 +0200 Subject: [PATCH 06/16] Some fixes in the IterativeIDESolver (#782) * Fix the IterativeIDESolver by allowing to analyze problems that override extend() and combine() * Use the logger in the IterativeIDESolver * Make it possible to solve the IDEFeatureTaintAnalysis with the IterativeIDESolver * Fix errors introduced by merge --- .../IfdsIde/Solver/IterativeIDESolver.h | 40 ++++++++++--------- .../Problems/IDEFeatureTaintAnalysis.h | 9 +++++ .../Problems/IDEFeatureTaintAnalysisTest.cpp | 22 +++++----- 3 files changed, 42 insertions(+), 29 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h index 68ee40cb80..9ae1b0a2db 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -474,7 +474,7 @@ class IterativeIDESolver return true; } - auto NewEF = EF.joinWith(std::move(LocalEF)); + auto NewEF = Problem.combine(EF, std::move(LocalEF)); assert(NewEF != nullptr); if (NewEF != EF) { @@ -540,7 +540,7 @@ class IterativeIDESolver return; } - auto NewEF = EF.joinWith(std::move(LocalEF)); + auto NewEF = Problem.combine(EF, std::move(LocalEF)); assert(NewEF != nullptr); if (NewEF != EF) { @@ -622,10 +622,11 @@ class IterativeIDESolver auto FactId = FactCompressor.getOrInsert(Fact); auto EF = [&] { if constexpr (ComputeValues) { - return SourceEF.composeWith(FECache.getNormalEdgeFunction( + auto NEF = FECache.getNormalEdgeFunction( Problem, AtInstruction, CSFact, Succ, Fact, combineIds(AtInstructionId, SuccId), - combineIds(PropagatedFactId, FactId))); + combineIds(PropagatedFactId, FactId)); + return Problem.extend(SourceEF, std::move(NEF)); } else { return EdgeFunctionPtrType{}; } @@ -694,10 +695,11 @@ class IterativeIDESolver auto EF = [&] { if constexpr (ComputeValues) { - return SourceEF.composeWith(FECache.getCallToRetEdgeFunction( + auto CEF = FECache.getCallToRetEdgeFunction( Problem, AtInstruction, CSFact, RetSite, Fact, Callees /*Vec*/, combineIds(AtInstructionId, RetSiteId), - combineIds(PropagatedFactId, FactId))); + combineIds(PropagatedFactId, FactId)); + return Problem.extend(SourceEF, std::move(CEF)); } else { return EdgeFunctionPtrType{}; } @@ -835,10 +837,11 @@ class IterativeIDESolver auto CallEF = [&] { if constexpr (ComputeValues) { - return SourceEF.composeWith(FECache.getCallEdgeFunction( + auto CEF = FECache.getCallEdgeFunction( Problem, AtInstruction, CSFact, Callee, Fact, combineIds(AtInstructionId, CalleeId), - combineIds(CSFactId, FactId))); + combineIds(CSFactId, FactId)); + return Problem.extend(SourceEF, std::move(CEF)); } else { return EdgeFunctionPtrType{}; } @@ -900,7 +903,7 @@ class IterativeIDESolver Problem, AtInstruction, CSFact, RetSite, Fact, combineIds(AtInstructionId, RetSiteId), combineIds(CSFactId, FactId)); - return EF ? SourceEF.composeWith(std::move(EF)) : SourceEF; + return EF ? Problem.extend(SourceEF, std::move(EF)) : SourceEF; } else { return EdgeFunctionPtrType{}; } @@ -939,8 +942,8 @@ class IterativeIDESolver Problem, CallSite, Callee, ExitInst, SummaryFact, RetSite, RetFact, ExitId, combineIds(CSId, RSId), combineIds(SummaryFactId, RetFactId)); - return CallEF.composeWith(Summary.second) - .composeWith(std::move(RetEF)); + return Problem.extend(Problem.extend(CallEF, Summary.second), + std::move(RetEF)); } else { return EdgeFunctionPtrType{}; } @@ -954,16 +957,15 @@ class IterativeIDESolver } void processInterJobs() { - - llvm::errs() << "processInterJobs: " << CallWL.size() - << " relevant calls\n"; + PHASAR_LOG_LEVEL(INFO, "processInterJobs: " << CallWL.size() + << " relevant calls"); /// Here, no other job is running concurrently, so we save and reset the /// CallWL, such that we can start concurrent jobs in the loop below std::vector RelevantCalls(CallWL.begin(), CallWL.end()); scope_exit FinishedInterCalls = [] { - llvm::errs() << "> end inter calls\n"; + PHASAR_LOG_LEVEL(INFO, "> end inter calls"); }; if constexpr (EnableStatistics) { @@ -1250,14 +1252,14 @@ class IterativeIDESolver } void runGC() { - llvm::errs() << "runGC() with " << CandidateFunctionsForGC.count() - << " candidates\n"; + PHASAR_LOG_LEVEL(INFO, "runGC() with " << CandidateFunctionsForGC.count() + << " candidates"); size_t NumCollectedFuns = 0; scope_exit FinishGC = [&NumCollectedFuns] { - llvm::errs() << "> Finished GC run (collected " << NumCollectedFuns - << " functions)\n"; + PHASAR_LOG_LEVEL(INFO, "> Finished GC run (collected " << NumCollectedFuns + << " functions)"); }; auto FinalCandidates = getCollectableFunctions(); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h index 9cca9dc15e..b021f7b5af 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.h @@ -374,4 +374,13 @@ class IDEFeatureTaintAnalysis } // namespace psr +namespace std { +template <> struct hash { + size_t + operator()(const psr::IDEFeatureTaintEdgeFact &EdgeFact) const noexcept { + return hash_value(EdgeFact); + } +}; +} // namespace std + #endif // PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_PROBLEMS_IDEFEATURETAINTANALYSIS_H diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp index 2b98d117b1..bce6226662 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysisTest.cpp @@ -129,7 +129,8 @@ class IDEFeatureTaintAnalysisTest : public ::testing::Test { *HA, EntryPoints, Generator); IDESolver IIASolver(IIAProblem, &HA->getICFG()); - IIASolver.solve(); + // IterativeIDESolver IIASolver(&IIAProblem, &HA->getICFG()); + auto Results = IIASolver.solve(); // do the comparison for (const auto &[InstLoc, VarName, ExpectedVal] : GroundTruth) { @@ -157,7 +158,7 @@ class IDEFeatureTaintAnalysisTest : public ::testing::Test { IIASolver.dumpResults(llvm::errs()); llvm::errs() << "\n======================================================\n"; - printDump(HA->getProjectIRDB(), IIASolver.getSolverResults()); + printDump(HA->getProjectIRDB(), Results); } } @@ -166,10 +167,10 @@ class IDEFeatureTaintAnalysisTest : public ::testing::Test { } // See vara::PhasarTaintAnalysis::taintsForInst - [[nodiscard]] inline TaintSetT - taintsForInst(const llvm::Instruction *Inst, - SolverResults SR) { + [[nodiscard]] inline TaintSetT taintsForInst( + const llvm::Instruction *Inst, + GenericSolverResults SR) { if (const auto *Ret = llvm::dyn_cast(Inst)) { if (Ret->getNumOperands() == 0) { @@ -205,10 +206,11 @@ class IDEFeatureTaintAnalysisTest : public ::testing::Test { return AggregatedTaints; } - void printDump(const LLVMProjectIRDB &IRDB, - SolverResults - SR) { + void + printDump(const LLVMProjectIRDB &IRDB, + GenericSolverResults + SR) { const llvm::Function *CurrFun = nullptr; for (const auto *Inst : IRDB.getAllInstructions()) { if (CurrFun != Inst->getFunction()) { From 85403fdb9fcaaccd825d685d30940408923c7a23 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Mon, 8 Sep 2025 17:15:04 +0200 Subject: [PATCH 07/16] Bump dependencies (#784) --- CMakeLists.txt | 2 +- external/json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fce4cd2407..4b14f22224 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -284,7 +284,7 @@ if (NOT PHASAR_IN_TREE) FetchContent_Declare( googletest GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG v1.16.0 + GIT_TAG v1.17.0 ) FetchContent_MakeAvailable(googletest) endif() diff --git a/external/json b/external/json index 9cca280a4d..55f93686c0 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 +Subproject commit 55f93686c01528224f448c19128836e7df245f72 From cce4fb6bacb7770fef1be6ca4a58b5662006ad71 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Thu, 11 Sep 2025 18:03:36 +0200 Subject: [PATCH 08/16] Remove boost from BitVectorSet (#788) * Remove boost from BitVectorSet * Minor deboostifying * Apply review comment on Bit-index-inbounds check + simplify operator<< --- .../DataFlow/IfdsIde/Solver/Compressor.h | 148 +---------- .../DataFlow/Mono/Contexts/CallStringCTX.h | 11 +- include/phasar/Utils/BitVectorSet.h | 246 ++++++------------ include/phasar/Utils/Compressor.h | 179 +++++++++++++ include/phasar/Utils/Fn.h | 4 +- lib/Utils/Utilities.cpp | 17 +- unittests/Utils/BitVectorSetTest.cpp | 6 +- 7 files changed, 274 insertions(+), 337 deletions(-) create mode 100644 include/phasar/Utils/Compressor.h diff --git a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h index d0f9472d9c..29c900e0bb 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/Compressor.h @@ -3,158 +3,12 @@ #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/Utils/ByRef.h" -#include "phasar/Utils/TypeTraits.h" - -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/DenseMapInfo.h" -#include "llvm/ADT/SmallVector.h" +#include "phasar/Utils/Compressor.h" #include -#include -#include -#include #include namespace psr { -template class Compressor; - -/// \brief A utility class that assigns a sequential Id to every inserted -/// object. -/// -/// This specialization handles types that can be efficiently passed by value -template -class Compressor>> { -public: - void reserve(size_t Capacity) { - assert(Capacity <= UINT32_MAX); - ToInt.reserve(Capacity); - FromInt.reserve(Capacity); - } - - uint32_t getOrInsert(T Elem) { - auto [It, Inserted] = ToInt.try_emplace(Elem, ToInt.size()); - if (Inserted) { - FromInt.push_back(Elem); - } - return It->second; - } - - std::optional getOrNull(T Elem) const { - if (auto It = ToInt.find(Elem); It != ToInt.end()) { - return It->second; - } - return std::nullopt; - } - - T operator[](size_t Idx) const noexcept { - assert(Idx < FromInt.size()); - return FromInt[Idx]; - } - - [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } - [[nodiscard]] size_t capacity() const noexcept { - return FromInt.capacity() + - ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); - } - - auto begin() const noexcept { return FromInt.begin(); } - auto end() const noexcept { return FromInt.end(); } - -private: - llvm::DenseMap ToInt; - llvm::SmallVector FromInt; -}; - -/// \brief A utility class that assigns a sequential Id to every inserted -/// object. -/// -/// This specialization handles types that cannot be efficiently passed by value -template -class Compressor>> { -public: - void reserve(size_t Capacity) { - assert(Capacity <= UINT32_MAX); - ToInt.reserve(Capacity); - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present yet, it will be added first and its index will - /// then be returned. - uint32_t getOrInsert(const T &Elem) { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - auto Ret = FromInt.size(); - auto *Ins = &FromInt.emplace_back(Elem); - ToInt[Ins] = Ret; - return Ret; - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present yet, it will be added first and its index will - /// then be returned. - uint32_t getOrInsert(T &&Elem) { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - auto Ret = FromInt.size(); - auto *Ins = &FromInt.emplace_back(std::move(Elem)); - ToInt[Ins] = Ret; - return Ret; - } - - /// Returns the index of the given element in the compressors storage. If the - /// element isn't present, std::nullopt will be returned - std::optional getOrNull(const T &Elem) const { - if (auto It = ToInt.find(&Elem); It != ToInt.end()) { - return It->second; - } - return std::nullopt; - } - - const T &operator[](size_t Idx) const noexcept { - assert(Idx < FromInt.size()); - return FromInt[Idx]; - } - - [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } - [[nodiscard]] size_t capacity() const noexcept { - return FromInt.size() + - ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); - } - - auto begin() const noexcept { return FromInt.begin(); } - auto end() const noexcept { return FromInt.end(); } - -private: - struct DSI : llvm::DenseMapInfo { - static auto getHashValue(const T *Elem) noexcept { - assert(Elem != nullptr); - if constexpr (has_llvm_dense_map_info) { - return llvm::DenseMapInfo::getHashValue(*Elem); - } else { - return std::hash{}(*Elem); - } - } - static auto isEqual(const T *LHS, const T *RHS) noexcept { - if (LHS == RHS) { - return true; - } - if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() || - RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) { - return false; - } - if constexpr (has_llvm_dense_map_info) { - return llvm::DenseMapInfo::isEqual(*LHS, *RHS); - } else { - return *LHS == *RHS; - } - } - }; - - std::deque FromInt; - llvm::DenseMap ToInt; -}; struct NoneCompressor final { constexpr NoneCompressor() noexcept = default; diff --git a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h index c7326729b8..42ac02b425 100644 --- a/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h +++ b/include/phasar/DataFlow/Mono/Contexts/CallStringCTX.h @@ -3,10 +3,9 @@ #include "phasar/Utils/Printer.h" +#include "llvm/ADT/Hashing.h" #include "llvm/Support/raw_ostream.h" -#include "boost/functional/hash.hpp" - #include #include #include @@ -94,11 +93,9 @@ namespace std { template struct hash> { size_t operator()(const psr::CallStringCTX &CS) const noexcept { - boost::hash> HashDeque; - std::hash HashUnsigned; - size_t U = HashUnsigned(K); - size_t H = HashDeque(CS.CallString); - return U ^ (H << 1); + auto H = + llvm::hash_combine_range(CS.CallString.begin(), CS.CallString.end()); + return llvm::hash_combine(K, H); } }; diff --git a/include/phasar/Utils/BitVectorSet.h b/include/phasar/Utils/BitVectorSet.h index b11786e847..3ed4fe474b 100644 --- a/include/phasar/Utils/BitVectorSet.h +++ b/include/phasar/Utils/BitVectorSet.h @@ -7,22 +7,25 @@ * Philipp Schubert and Richard Leer *****************************************************************************/ -#ifndef PHASAR_UTILS_BITVECTORSET_H_ -#define PHASAR_UTILS_BITVECTORSET_H_ +#ifndef PHASAR_UTILS_BITVECTORSET_H +#define PHASAR_UTILS_BITVECTORSET_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Compressor.h" +#include "phasar/Utils/Fn.h" #include "llvm/ADT/BitVector.h" #include "llvm/ADT/Hashing.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/raw_ostream.h" -#include "boost/bimap.hpp" -#include "boost/bimap/unordered_set_of.hpp" - #include #include #include #include +#include namespace psr { namespace internal { @@ -61,122 +64,68 @@ inline llvm::ArrayRef getWords(const llvm::SmallBitVector &BV, } } // namespace internal -/** - * BitVectorSet implements a set that requires minimal space. Elements are - * kept in a static map and the set itself only stores a vector of bits which - * indicate whether elements are contained in the set. - * - * @brief Implements a set that requires minimal space. - */ +/// BitVectorSet implements a set that requires minimal space. Elements are +/// kept in a static map and the set itself only stores a vector of bits which +/// indicate whether elements are contained in the set. +/// +/// \brief Implements a set that requires minimal space. +/// \attention This data-structure is NOT thread-safe, since it relies on static +/// storage! +/// template class BitVectorSet { -public: - // Using boost::hash causes ambiguity for hash_value(): - // - - // - - // - - using bimap_t = boost::bimap>, - boost::bimaps::unordered_set_of>; -private: - inline static bimap_t Position; // NOLINT - BitVectorTy Bits; + static ByConstRef iterTransform(uint32_t Idx) { + assert(Position.inbounds(Idx)); + return Position[Idx]; + } - template class BitVectorSetIterator { - BitVectorTy Bits; + // using IteratorTy = + // llvm::mapped_iterator>; + // Unfortunately cannot use llvm::mapped_iterator, since the + // const_set_bits_iterator does not properly implement the iterator interface + // (typename iterator_category is missing) + class IteratorTy { public: + using value_type = T; + using reference = ByConstRef; + using pointer = const T *; + using difference_type = ptrdiff_t; using iterator_category = std::forward_iterator_tag; - using value_type = D; - using difference_type = std::ptrdiff_t; - using pointer = D *; - using reference = D &; - BitVectorSetIterator(D Ptr = nullptr) : PosPtr(Ptr) {} - - BitVectorSetIterator &operator=(D *Ptr) { - PosPtr = Ptr; - return *this; - } - - void setBits(const BitVectorTy &OtherBits) { Bits = OtherBits; } - bool operator==(const BitVectorSetIterator &OtherIterator) const { - return PosPtr == OtherIterator.getPtr(); - } - - bool operator!=(const BitVectorSetIterator &OtherIterator) const { - return !(*this == OtherIterator); - } - - BitVectorSetIterator &operator+=(const difference_type &Movement) { - for (difference_type I = 0; I < Movement; I++) { - PosPtr++; - } - return *this; - } - - BitVectorSetIterator &operator++() { - do { - int NextIdx = Bits.find_next(PosPtr->first); - - if (NextIdx <= static_cast(PosPtr->first)) { - PosPtr = Position.right.find(Bits.size()); - break; - } - - PosPtr = Position.right.find(NextIdx); - - assert(PosPtr->first < Bits.size() && - "pos_ptr->first index into BitVector out of range"); - } while (!Bits[PosPtr->first]); + constexpr IteratorTy(typename BitVectorTy::const_set_bits_iterator It, + fn_t Txn = {}) noexcept + : It(std::move(It)), Txn(Txn) {} + IteratorTy &operator++() { + ++It; return *this; } - - BitVectorSetIterator operator++(int) { - auto Temp(*this); + IteratorTy operator++(int) { + auto Cpy = *this; ++*this; - return Temp; + return Cpy; } - BitVectorSetIterator operator+(const difference_type &Movement) { - auto OldPtr = PosPtr; - for (difference_type I = 0; I < Movement; I++) { - PosPtr++; - } - auto Temp(*this); - PosPtr = OldPtr; - return Temp; - } + [[nodiscard]] reference operator*() const { return Txn(*It); } - difference_type operator-(const BitVectorSetIterator &OtherIterator) { - return std::distance(OtherIterator.getPtr(), this->getPtr()); + [[nodiscard]] bool operator==(const IteratorTy &Other) const { + return It == Other.It; + } + [[nodiscard]] bool operator!=(const IteratorTy &Other) const { + return !(*this == Other); } - - // T& operator*(){return pos_ptr->second;} - - const T &operator*() const { return PosPtr->second; } - - D *operator->() { return PosPtr; } - - [[nodiscard]] D getPtr() const { return PosPtr; } - - // const D* getConstPtr()const{return pos_ptr;} - - // T getPos() {return pos_ptr->first;} - - // T getVal() {return pos_ptr->second;} - - [[nodiscard]] const BitVectorTy &getBits() const { return Bits; } private: - D PosPtr; + typename BitVectorTy::const_set_bits_iterator It; + [[no_unique_address]] fn_t Txn; }; public: - using iterator = BitVectorSetIterator; - using const_iterator = - BitVectorSetIterator; + using const_iterator = IteratorTy; + using iterator = const_iterator; using value_type = T; BitVectorSet() = default; @@ -187,11 +136,12 @@ class BitVectorSet { insert(IList.begin(), IList.end()); } - template BitVectorSet(InputIt First, InputIt Last) { + template + explicit BitVectorSet(InputIt First, InputIt Last) { insert(First, Last); } - static BitVectorSet fromBits(BitVectorTy Bits) { + [[nodiscard]] static BitVectorSet fromBits(BitVectorTy Bits) { BitVectorSet Ret; Ret.Bits = std::move(Bits); return Ret; @@ -225,22 +175,13 @@ class BitVectorSet { } void insert(const T &Data) { - auto Search = Position.left.find(Data); - // Data already known - if (Search != Position.left.end()) { - if (Bits.size() <= Search->second) { - Bits.resize(Search->second + 1); - } - Bits.set(Search->second); - } else { - // Data unknown - size_t Idx = Position.left.size(); - Position.left.insert(std::make_pair(Data, Position.left.size())); - if (Bits.size() <= Position.left.size()) { - Bits.resize(Position.left.size()); - } - Bits.set(Idx); + uint32_t Idx = Position.getOrInsert(Data); + + if (Idx >= Bits.size()) { + Bits.resize(Idx + 1); } + + Bits.set(Idx); } void insert(const BitVectorSet &Other) { Bits |= Other.Bits; } @@ -253,10 +194,9 @@ class BitVectorSet { } void erase(const T &Data) noexcept { - auto Search = Position.left.find(Data); - if (Search != Position.left.end()) { - if (Bits.size() > Search->second) { - Bits.reset(Search->second); + if (auto Idx = Position.getOrNull(Data)) { + if (*Idx < Bits.size()) { + Bits.reset(*Idx); } } } @@ -281,12 +221,10 @@ class BitVectorSet { [[nodiscard]] bool find(const T &Data) const noexcept { return count(Data); } [[nodiscard]] size_t count(const T &Data) const noexcept { - auto Search = Position.left.find(Data); - if (Search != Position.left.end()) { - if (Bits.size() > Search->second) { - return Bits[Search->second]; - } + if (auto Idx = Position.getOrNull(Data)) { + return (*Idx < Bits.size() && Bits.test(*Idx)) ? 1 : 0; } + return 0; } @@ -347,56 +285,28 @@ class BitVectorSet { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const BitVectorSet &B) { OS << '<'; - size_t Idx = 0; - for (auto &Position : B.Position.left) { - if (Position.second < B.Bits.size() && B.Bits[Position.second]) { - ++Idx; - OS << Position.first; - if (Idx < B.size()) { - OS << ", "; - } - } - } - OS << '>'; - return OS; + llvm::interleaveComma(B, OS); + return OS << '>'; } - [[nodiscard]] iterator begin() { - int Index = Bits.find_first(); - if (Index == -1) { - Index = Bits.size(); - } - iterator BeginIter(Position.right.find(Index)); - BeginIter.setBits(Bits); - return BeginIter; + [[nodiscard]] const_iterator begin() const noexcept { + return {Bits.set_bits_begin(), {}}; } - - [[nodiscard]] iterator end() { - iterator EndIter(Position.right.find(Bits.size())); - EndIter.setBits(Bits); - return EndIter; + [[nodiscard]] const_iterator end() const noexcept { + return {Bits.set_bits_end(), {}}; } - [[nodiscard]] const_iterator begin() const { - int Index = Bits.find_first(); - if (Index == -1) { - Index = Bits.size(); - } - const_iterator BeginIter(Position.right.find(Index)); - BeginIter.setBits(Bits); - return BeginIter; + [[nodiscard]] iterator begin() noexcept { + return {Bits.set_bits_begin(), {}}; } + [[nodiscard]] iterator end() noexcept { return {Bits.set_bits_end(), {}}; } - [[nodiscard]] const_iterator end() const { - const_iterator EndIter(Position.right.find(Bits.size())); - EndIter.setBits(Bits); - return EndIter; - } + static void clearPosition() noexcept { Position.clear(); } - static void clearPosition() { - Position.left.clear(); - Position.right.clear(); - } +private: + inline static Compressor Position; + + BitVectorTy Bits; }; // Overloads with the other intersectWith functions from Utilities.h diff --git a/include/phasar/Utils/Compressor.h b/include/phasar/Utils/Compressor.h new file mode 100644 index 0000000000..296dee5e08 --- /dev/null +++ b/include/phasar/Utils/Compressor.h @@ -0,0 +1,179 @@ +#ifndef PHASAR_UTILS_COMPRESSOR_H +#define PHASAR_UTILS_COMPRESSOR_H + +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/TypeTraits.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include +#include +#include +#include + +namespace psr { +template +class Compressor; + +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that can be efficiently passed by value +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + FromInt.reserve(Capacity); + } + + IdT getOrInsert(T Elem) { + auto [It, Inserted] = ToInt.try_emplace(Elem, IdT(ToInt.size())); + if (Inserted) { + FromInt.push_back(Elem); + } + return It->second; + } + + std::optional getOrNull(T Elem) const { + if (auto It = ToInt.find(Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + [[nodiscard]] bool inbounds(IdT Idx) const noexcept { + return size_t(Idx) < FromInt.size(); + } + + T operator[](IdT Idx) const noexcept { + assert(inbounds(Idx)); + return FromInt[size_t(Idx)]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.capacity() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + + void clear() noexcept { + ToInt.clear(); + FromInt.clear(); + } + +private: + llvm::DenseMap ToInt; + llvm::SmallVector FromInt; +}; + +/// \brief A utility class that assigns a sequential Id to every inserted +/// object. +/// +/// This specialization handles types that cannot be efficiently passed by value +template +class Compressor>> { +public: + void reserve(size_t Capacity) { + assert(Capacity <= UINT32_MAX); + ToInt.reserve(Capacity); + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. + IdT getOrInsert(const T &Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = IdT(FromInt.size()); + auto *Ins = &FromInt.emplace_back(Elem); + ToInt[Ins] = Ret; + return Ret; + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present yet, it will be added first and its index will + /// then be returned. + IdT getOrInsert(T &&Elem) { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + auto Ret = IdT(FromInt.size()); + auto *Ins = &FromInt.emplace_back(std::move(Elem)); + ToInt[Ins] = Ret; + return Ret; + } + + /// Returns the index of the given element in the compressors storage. If the + /// element isn't present, std::nullopt will be returned + std::optional getOrNull(const T &Elem) const { + if (auto It = ToInt.find(&Elem); It != ToInt.end()) { + return It->second; + } + return std::nullopt; + } + + [[nodiscard]] bool inbounds(IdT Idx) const noexcept { + return size_t(Idx) < FromInt.size(); + } + + const T &operator[](IdT Idx) const noexcept { + assert(inbounds(Idx)); + return FromInt[size_t(Idx)]; + } + + [[nodiscard]] size_t size() const noexcept { return FromInt.size(); } + [[nodiscard]] size_t capacity() const noexcept { + return FromInt.size() + + ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type); + } + + auto begin() const noexcept { return FromInt.begin(); } + auto end() const noexcept { return FromInt.end(); } + + void clear() noexcept { + ToInt.clear(); + FromInt.clear(); + } + +private: + struct DSI : llvm::DenseMapInfo { + static auto getHashValue(const T *Elem) noexcept { + assert(Elem != nullptr); + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::getHashValue(*Elem); + } else { + return std::hash{}(*Elem); + } + } + static auto isEqual(const T *LHS, const T *RHS) noexcept { + if (LHS == RHS) { + return true; + } + if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() || + RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) { + return false; + } + if constexpr (has_llvm_dense_map_info) { + return llvm::DenseMapInfo::isEqual(*LHS, *RHS); + } else { + return *LHS == *RHS; + } + } + }; + + std::deque FromInt; + llvm::DenseMap ToInt; +}; + +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H diff --git a/include/phasar/Utils/Fn.h b/include/phasar/Utils/Fn.h index 55cc644149..060a2f7b77 100644 --- a/include/phasar/Utils/Fn.h +++ b/include/phasar/Utils/Fn.h @@ -24,8 +24,8 @@ namespace psr { template struct fn_t { // NOLINT(readability-identifier-naming) template constexpr std::invoke_result_t - operator()(ArgsT &&...Args) noexcept( - std::is_nothrow_invocable_v) { + operator()(ArgsT &&...Args) const + noexcept(std::is_nothrow_invocable_v) { return std::invoke(F, PSR_FWD(Args)...); } }; diff --git a/lib/Utils/Utilities.cpp b/lib/Utils/Utilities.cpp index 0c2aeedaca..0689e09d70 100644 --- a/lib/Utils/Utilities.cpp +++ b/lib/Utils/Utilities.cpp @@ -12,11 +12,10 @@ #include "phasar/Utils/Logger.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/DerivedTypes.h" -#include "boost/algorithm/string/find.hpp" - #include #include @@ -43,21 +42,19 @@ bool isConstructor(llvm::StringRef MangledName) { // see https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling // This version will not work in some edge cases - auto Constructor = boost::algorithm::find_last(MangledName, "C2E"); - if (Constructor.begin() != Constructor.end()) { + auto Constructor = MangledName.rfind("C2E"); + if (Constructor != llvm::StringRef::npos) { return true; } - Constructor = boost::algorithm::find_last(MangledName, "C1E"); - - if (Constructor.begin() != Constructor.end()) { + Constructor = MangledName.rfind("C1E"); + if (Constructor != llvm::StringRef::npos) { return true; } - Constructor = boost::algorithm::find_last(MangledName, "C2E"); - - if (Constructor.begin() != Constructor.end()) { + Constructor = MangledName.rfind("C3E"); + if (Constructor != llvm::StringRef::npos) { return true; } diff --git a/unittests/Utils/BitVectorSetTest.cpp b/unittests/Utils/BitVectorSetTest.cpp index 9f9a073b03..f85dba2d8b 100644 --- a/unittests/Utils/BitVectorSetTest.cpp +++ b/unittests/Utils/BitVectorSetTest.cpp @@ -6,6 +6,7 @@ #include "gtest/gtest.h" +#include #include #include #include @@ -345,8 +346,8 @@ TEST(BitVectorSet, iterator_movement) { auto IteratorA = A.begin(); auto IteratorB = B.begin(); - IteratorA += 4; - IteratorB += 2; + std::advance(IteratorA, 4); + std::advance(IteratorB, 2); EXPECT_EQ(*IteratorA, *IteratorB); EXPECT_EQ(A.count(*IteratorA), 1U); IteratorB++; @@ -382,7 +383,6 @@ TEST(BitVectorSet, rangeFor) { const BitVectorSet D({5, 6, 7, 8, 42, 13}); std::set DS; std::set DSGT = {5, 6, 7, 8, 42, 13}; - auto I = D.begin(); for (auto I : D) { DS.insert(I); } From cb87890de77b156db4fb32e441959f4f5e97c90c Mon Sep 17 00:00:00 2001 From: Fabian Schiebel <52407375+fabianbs96@users.noreply.github.com> Date: Sun, 12 Oct 2025 10:17:41 +0200 Subject: [PATCH 09/16] GCC Compatibility (#787) * Make phasar compile with gcc (g++-11) and uncover (and fix) two bugs related to json ser/de with LLVMAliasSet and LLVMBasedICFG that were not detected untio now, because of nlohmann's implicit conversions feature * Address review comments * Simplify isLLVMZeroValue according to the discussion in #787 --- include/phasar/ControlFlow/CallGraphBase.h | 4 +- .../phasar/DataFlow/IfdsIde/EdgeFunction.h | 21 +++++++--- .../phasar/DataFlow/IfdsIde/FlowFunctions.h | 2 +- .../ControlFlow/LLVMBasedBackwardICFG.h | 3 ++ .../PhasarLLVM/ControlFlow/LLVMBasedICFG.h | 3 ++ .../ControlFlow/SparseLLVMBasedCFG.h | 2 + .../ControlFlow/SparseLLVMBasedICFG.h | 5 ++- .../ControlFlow/SparseLLVMBasedICFGView.h | 3 ++ .../IfdsIde/DefaultAliasAwareIDEProblem.h | 9 ++++ .../IfdsIde/DefaultNoAliasIDEProblem.h | 10 +++++ ...efaultReachableAllocationSitesIDEProblem.h | 10 +++++ .../DataFlow/IfdsIde/LLVMZeroValue.h | 20 ++++++--- .../Problems/IDEInstInteractionAnalysis.h | 42 +++++++++++-------- .../IfdsIde/Problems/IDETypeStateAnalysis.h | 5 ++- include/phasar/PhasarLLVM/HelperAnalyses.h | 13 +++--- .../phasar/PhasarLLVM/HelperAnalysisConfig.h | 10 ++--- .../phasar/PhasarLLVM/Pointer/LLVMAliasSet.h | 2 +- lib/AnalysisStrategy/Strategies.cpp | 2 + lib/ControlFlow/CallGraphAnalysisType.cpp | 1 + .../ControlFlow/SparseLLVMBasedICFG.cpp | 3 +- .../Problems/IDEFeatureTaintAnalysis.cpp | 4 +- lib/PhasarLLVM/HelperAnalyses.cpp | 5 ++- .../Passes/GeneralStatisticsAnalysis.cpp | 2 + lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp | 21 +++------- lib/Utils/NlohmannLogging.cpp | 2 +- tools/phasar-cli/phasar-cli.cpp | 10 +++-- .../IfdsIde/DefaultFlowFunctionTest.cpp | 4 ++ .../Pointer/LLVMAliasSetSerializationTest.cpp | 4 +- 28 files changed, 149 insertions(+), 73 deletions(-) diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h index 67bfb0cc94..1bc44b603c 100644 --- a/include/phasar/ControlFlow/CallGraphBase.h +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -37,7 +37,7 @@ template class CallGraphBase : public CRTPBase { /// NOTE: This function is typically called in a hot part of the analysis and /// should therefore be very fast [[nodiscard]] decltype(auto) getCalleesOfCallAt(ByConstRef Inst) const - noexcept(noexcept(self().getCalleesOfCallAtImpl(Inst))) { + noexcept(noexcept(this->self().getCalleesOfCallAtImpl(Inst))) { static_assert( is_iterable_over_v); return self().getCalleesOfCallAtImpl(Inst); @@ -47,7 +47,7 @@ template class CallGraphBase : public CRTPBase { /// call the given function induced by the used call-graph. [[nodiscard]] decltype(auto) getCallersOf(ByConstRef Fun) const { static_assert( - is_iterable_over_v); + is_iterable_over_vself().getCallersOfImpl(Fun)), n_t>); return self().getCallersOfImpl(Fun); } }; diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 0559e17afe..5e06b89e12 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -442,21 +442,32 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { typename = std::enable_if_t< !std::is_same_v> && IsEdgeFunction>> - [[nodiscard]] friend bool operator==(EdgeFunctionRef LHS, - const EdgeFunction &RHS) noexcept { - if (!RHS.template isa()) { + [[nodiscard]] bool equals(EdgeFunctionRef Other) const noexcept { + // NOTE: Workaround issue in g++ that does not allow transitive friends: If + // putting this code in the operator== below, we cannot access + // Other.Instance, although it is friended... + if (!isa()) { return false; } - if (LHS.Instance == RHS.EF) { + if (Other.Instance == EF) { return true; } if constexpr (IsEqualityComparable) { - return *LHS == *getPtr(RHS.EF); + return *Other == *getPtr(EF); } else { return true; } } + template > && + IsEdgeFunction>> + [[nodiscard]] friend bool operator==(EdgeFunctionRef LHS, + const EdgeFunction &RHS) noexcept { + return RHS.equals(LHS); + } + template > && diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index 6e54fe3936..783a5a3e91 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -73,7 +73,7 @@ template > class FlowFunction { template struct IsFlowFunction { template static std::true_type test(const FlowFunction &); - static std::false_type test(...) {} + static std::false_type test(...); static constexpr bool value = // NOLINT std::is_same_v { friend ICFGBase; public: + using typename ICFGBase::f_t; + using typename ICFGBase::n_t; + // For backward compatibility static constexpr llvm::StringLiteral GlobalCRuntimeModelName = GlobalCtorsDtorsModel::ModelName; diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h index 6524bfad04..b4e41c2c44 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedCFG.h @@ -30,6 +30,8 @@ class SparseLLVMBasedCFG : public LLVMBasedCFG, friend struct SVFGCache; friend SparseCFGBase; + using typename LLVMBasedCFG::n_t; + public: using vgraph_t = llvm::SmallDenseMap; diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h index d9a1d9a931..f2fc45c0b5 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h @@ -33,6 +33,9 @@ class SparseLLVMBasedICFG friend SparseLLVMBasedCFGProvider; public: + using typename LLVMBasedICFG::f_t; + using typename LLVMBasedICFG::n_t; + /// Constructor that delegates all arguments to the ctor of LLVMBasedICFG explicit SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, CallGraphAnalysisType CGType, @@ -47,7 +50,7 @@ class SparseLLVMBasedICFG LLVMAliasInfoRef PT); explicit SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedCG, + const CallGraphData &SerializedCG, LLVMAliasInfoRef PT); ~SparseLLVMBasedICFG(); diff --git a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h index 25b773d675..303bee8fb3 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h +++ b/include/phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFGView.h @@ -45,6 +45,9 @@ class SparseLLVMBasedICFGView friend SparseLLVMBasedCFGProvider; public: + using typename LLVMBasedCFG::f_t; + using typename LLVMBasedCFG::n_t; + explicit SparseLLVMBasedICFGView(const LLVMBasedICFG *ICF, LLVMAliasInfoRef PT); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h index aac4390f97..0f1214504d 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultAliasAwareIDEProblem.h @@ -65,6 +65,10 @@ class DefaultAliasAwareIDEProblem protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { public: using typename IDETabulationProblem::db_t; + using typename IDETabulationProblem::n_t; + using typename IDETabulationProblem::f_t; + using typename IDETabulationProblem::d_t; + using typename IDETabulationProblem::FlowFunctionPtrType; using detail::IDEAliasAwareDefaultFlowFunctionsImpl::getAliasInfo; @@ -110,6 +114,11 @@ class DefaultAliasAwareIFDSProblem : public IFDSTabulationProblem, protected detail::IDEAliasAwareDefaultFlowFunctionsImpl { public: + using typename IFDSTabulationProblem::d_t; + using typename IFDSTabulationProblem::f_t; + using typename IFDSTabulationProblem::FlowFunctionPtrType; + using typename IFDSTabulationProblem::n_t; + /// Constructs an IFDSTabulationProblem with the usual arguments + alias /// information. /// diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h index 3bb35b42ca..19c00ac008 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultNoAliasIDEProblem.h @@ -59,6 +59,10 @@ class DefaultNoAliasIDEProblem public: using IDETabulationProblem::IDETabulationProblem; + using typename IDETabulationProblem::f_t; + using typename IDETabulationProblem::FlowFunctionPtrType; + using typename IDETabulationProblem::n_t; + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { return getNormalFlowFunctionImpl(Curr, Succ); @@ -89,6 +93,12 @@ class DefaultNoAliasIFDSProblem public: using IFDSTabulationProblem::IFDSTabulationProblem; + using typename IFDSTabulationProblem::d_t; + using typename IFDSTabulationProblem::f_t; + using typename IFDSTabulationProblem::FlowFunctionPtrType; + using typename IFDSTabulationProblem::l_t; + using typename IFDSTabulationProblem::n_t; + [[nodiscard]] FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) override { return getNormalFlowFunctionImpl(Curr, Succ); diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h index eac661ccd4..2f127432c4 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/DefaultReachableAllocationSitesIDEProblem.h @@ -66,6 +66,10 @@ class DefaultReachableAllocationSitesIDEProblem protected detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl { public: using typename IDETabulationProblem::db_t; + using typename IDETabulationProblem::d_t; + using typename IDETabulationProblem::f_t; + using typename IDETabulationProblem::n_t; + using typename IDETabulationProblem::FlowFunctionPtrType; /// Constructs an IDETabulationProblem with the usual arguments + alias /// information. @@ -109,6 +113,12 @@ class DefaultReachableAllocationSitesIFDSProblem : public IFDSTabulationProblem, protected detail::IDEReachableAllocationSitesDefaultFlowFunctionsImpl { public: + using typename IFDSTabulationProblem::d_t; + using typename IFDSTabulationProblem::db_t; + using typename IFDSTabulationProblem::f_t; + using typename IFDSTabulationProblem::FlowFunctionPtrType; + using typename IFDSTabulationProblem::n_t; + /// Constructs an IFDSTabulationProblem with the usual arguments + alias /// information. /// diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h index 729e3c44a5..2d3b2ae27c 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h @@ -17,6 +17,8 @@ #ifndef PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMZEROVALUE_H #define PHASAR_PHASARLLVM_DATAFLOW_IFDSIDE_LLVMZEROVALUE_H +#include "phasar/Utils/Fn.h" + #include "llvm/ADT/StringRef.h" #include "llvm/IR/GlobalVariable.h" @@ -36,6 +38,9 @@ class LLVMZeroValue : public llvm::GlobalVariable { LLVMZeroValue(llvm::Module &Mod); static constexpr llvm::StringLiteral LLVMZeroValueInternalName = "zero_value"; + static bool isZeroValueImpl(const llvm::Value *V) noexcept { + return V == getInstance(); + } public: LLVMZeroValue(const LLVMZeroValue &Z) = delete; @@ -48,13 +53,18 @@ class LLVMZeroValue : public llvm::GlobalVariable { return LLVMZeroValueInternalName; } - // Do not specify a destructor (at all)! - static const LLVMZeroValue *getInstance(); + /// Gets the singleton instance of the special zero value (aka. Λ). + [[nodiscard]] static const LLVMZeroValue *getInstance(); + /// Checks, whether the given llvm::Value * is the special zero-value (aka. + /// Λ). + /// + /// You can use this as follows: + /// \code + /// return strongUpdateStore(Store, LLVMZeroValue::isLLVMZeroValue); + /// \endcode // NOLINTNEXTLINE(readability-identifier-naming) - static constexpr auto isLLVMZeroValue = [](const llvm::Value *V) noexcept { - return V == getInstance(); - }; + static constexpr auto isLLVMZeroValue = fn; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h index 4bfe3ecccd..01518d555e 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h @@ -969,17 +969,6 @@ class IDEInstInteractionAnalysisT [[nodiscard]] bool isConstant() const noexcept { return true; } - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const IIAAKillOrReplaceEF &EF) { - OS << "EF: (IIAAKillOrReplaceEF)<->"; - if (EF.isKillAll()) { - OS << "(KillAll"; - } else { - IDEInstInteractionAnalysisT::printEdgeFactImpl(OS, EF.Replacement); - } - return OS << ")"; - } - [[nodiscard]] bool isKillAll() const noexcept { if (auto *RSet = std::get_if>(&Replacement)) { return RSet->empty(); @@ -993,6 +982,20 @@ class IDEInstInteractionAnalysisT } }; + // Note: Having this operator a friend of the IDEInstInteractionAnalysisT + // (instead of IIAAKillOrReplaceEF) is required for gcc; otherwise, it cannot + // call the protected function printEdgeFactImpl. + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const IIAAKillOrReplaceEF &EF) { + OS << "EF: (IIAAKillOrReplaceEF)<->"; + if (EF.isKillAll()) { + OS << "(KillAll"; + } else { + IDEInstInteractionAnalysisT::printEdgeFactImpl(OS, EF.Replacement); + } + return OS << ")"; + } + // Edge function that adds the given labels to existing labels // add all labels provided by Data. struct IIAAAddLabelsEF { @@ -1018,19 +1021,22 @@ class IDEInstInteractionAnalysisT return Data == Other.Data; } - friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, - const IIAAAddLabelsEF &EF) { - OS << "EF: (IIAAAddLabelsEF: "; - IDEInstInteractionAnalysisT::printEdgeFactImpl(OS, EF.Data); - return OS << ")"; - } - // NOLINTNEXTLINE(readability-identifier-naming) -- needed for ADL friend llvm::hash_code hash_value(const IIAAAddLabelsEF &EF) { return hash_value(EF.Data); } }; + // Note: Having this operator a friend of the IDEInstInteractionAnalysisT + // (instead of IIAAAddLabelsEF) is required for gcc; otherwise, it cannot + // call the protected function printEdgeFactImpl. + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const IIAAAddLabelsEF &EF) { + OS << "EF: (IIAAAddLabelsEF: "; + IDEInstInteractionAnalysisT::printEdgeFactImpl(OS, EF.Data); + return OS << ")"; + } + const auto &getData(const EdgeFunction &EF) { if (const auto *AddLabels = llvm::dyn_cast(EF)) { return AddLabels->Data; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h index a09c1a63b1..2ad53bb9a2 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDETypeStateAnalysis.h @@ -19,6 +19,7 @@ #include "phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMZeroValue.h" #include "phasar/PhasarLLVM/Domain/LLVMAnalysisDomain.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Printer.h" @@ -41,7 +42,7 @@ namespace detail { class IDETypeStateAnalysisBaseCommon : public LLVMAnalysisDomainDefault { public: using container_type = std::set; - using FlowFunctionPtrType = FlowFunctionPtrType; + using FlowFunctionPtrType = psr::FlowFunctionPtrType; }; class IDETypeStateAnalysisBase @@ -217,7 +218,7 @@ class IDETypeStateAnalysis }; struct TSEdgeFunction { - using l_t = l_t; + using l_t = IDETypeStateAnalysis::l_t; const TypeStateDescriptionTy *TSD{}; // XXX: Do we really need a string here? Can't we just use an integer or sth // else that is cheap? diff --git a/include/phasar/PhasarLLVM/HelperAnalyses.h b/include/phasar/PhasarLLVM/HelperAnalyses.h index 2c6f06000b..c3287ffe94 100644 --- a/include/phasar/PhasarLLVM/HelperAnalyses.h +++ b/include/phasar/PhasarLLVM/HelperAnalyses.h @@ -11,13 +11,12 @@ #define PHASAR_PHASARLLVM_HELPERANALYSES_H #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/HelperAnalysisConfig.h" - -#include "nlohmann/json.hpp" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include #include -#include #include namespace llvm { @@ -34,10 +33,10 @@ class LLVMAliasSet; class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) public: explicit HelperAnalyses(std::string IRFile, - std::optional PrecomputedPTS, + std::optional PrecomputedPTS, AliasAnalysisType PTATy, bool AllowLazyPTS, std::vector EntryPoints, - std::optional PrecomputedCG, + std::optional PrecomputedCG, CallGraphAnalysisType CGTy, Soundness SoundnessLevel, bool AutoGlobalSupport) noexcept; @@ -75,12 +74,12 @@ class HelperAnalyses { // NOLINT(cppcoreguidelines-special-member-functions) std::string IRFile; // PTS - std::optional PrecomputedPTS; + std::optional PrecomputedPTS; AliasAnalysisType PTATy{}; bool AllowLazyPTS{}; // ICF - std::optional PrecomputedCG; + std::optional PrecomputedCG; std::vector EntryPoints; CallGraphAnalysisType CGTy{}; Soundness SoundnessLevel{}; diff --git a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h index df59c41c52..be3427667b 100644 --- a/include/phasar/PhasarLLVM/HelperAnalysisConfig.h +++ b/include/phasar/PhasarLLVM/HelperAnalysisConfig.h @@ -11,17 +11,17 @@ #define PHASAR_PHASARLLVM_HELPERANALYSISCONFIG_H #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphData.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Utils/Soundness.h" -#include "nlohmann/json.hpp" - #include namespace psr { struct HelperAnalysisConfig { - std::optional PrecomputedPTS = std::nullopt; - std::optional PrecomputedCG = std::nullopt; + std::optional PrecomputedPTS = std::nullopt; + std::optional PrecomputedCG = std::nullopt; AliasAnalysisType PTATy = AliasAnalysisType::CFLAnders; CallGraphAnalysisType CGTy = CallGraphAnalysisType::OTF; Soundness SoundnessLevel = Soundness::Soundy; @@ -31,7 +31,7 @@ struct HelperAnalysisConfig { /// existing llvm::Module bool PreprocessExistingModule = true; - HelperAnalysisConfig &&withCGType(CallGraphAnalysisType CGTy) &&noexcept { + HelperAnalysisConfig &&withCGType(CallGraphAnalysisType CGTy) && noexcept { this->CGTy = CGTy; return std::move(*this); } diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index eeffe7412a..5b99be1654 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -63,7 +63,7 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, /// Loads alias sets from JSON explicit LLVMAliasSet(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedPTS); + const LLVMAliasSetData &SerializedPTS); [[nodiscard]] inline bool isInterProcedural() const noexcept { return false; diff --git a/lib/AnalysisStrategy/Strategies.cpp b/lib/AnalysisStrategy/Strategies.cpp index 844b4e650c..9b99ef33be 100644 --- a/lib/AnalysisStrategy/Strategies.cpp +++ b/lib/AnalysisStrategy/Strategies.cpp @@ -10,6 +10,7 @@ #include "phasar/AnalysisStrategy/Strategies.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" namespace psr { @@ -24,6 +25,7 @@ std::string toString(const AnalysisStrategy &S) { case AnalysisStrategy::None: return "None"; } + llvm_unreachable("All alternatives should be handled by the switch above"); } AnalysisStrategy toAnalysisStrategy(llvm::StringRef S) { diff --git a/lib/ControlFlow/CallGraphAnalysisType.cpp b/lib/ControlFlow/CallGraphAnalysisType.cpp index 64473cc95e..e244e28a86 100644 --- a/lib/ControlFlow/CallGraphAnalysisType.cpp +++ b/lib/ControlFlow/CallGraphAnalysisType.cpp @@ -21,6 +21,7 @@ std::string psr::toString(CallGraphAnalysisType CGA) { case CallGraphAnalysisType::Invalid: return "Invalid"; } + llvm_unreachable("All alternatives should be handled by the switch above"); } psr::CallGraphAnalysisType psr::toCallGraphAnalysisType(llvm::StringRef S) { diff --git a/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.cpp b/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.cpp index ccaf80b2cf..2ef09c1a3d 100644 --- a/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.cpp @@ -1,5 +1,6 @@ #include "phasar/PhasarLLVM/ControlFlow/SparseLLVMBasedICFG.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "SVFGCache.h" @@ -32,7 +33,7 @@ SparseLLVMBasedICFG::SparseLLVMBasedICFG(CallGraph CG, AliasAnalysis(PT) {} SparseLLVMBasedICFG::SparseLLVMBasedICFG(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedCG, + const CallGraphData &SerializedCG, LLVMAliasInfoRef PT) : LLVMBasedICFG(IRDB, SerializedCG), SparseCFGCache(new SVFGCache{}), AliasAnalysis(PT) {} diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp index 1941ba9ef6..729a493b67 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp @@ -357,11 +357,11 @@ struct IDEFeatureTaintAnalysis::GenerateEF { llvm::report_fatal_error("Implemented in 'combine'"); } - constexpr friend bool operator==(const GenerateEF &L, const GenerateEF &R) { + friend bool operator==(const GenerateEF &L, const GenerateEF &R) { return L.Facts == R.Facts; } - constexpr friend llvm::hash_code hash_value(const GenerateEF &EF) { + friend llvm::hash_code hash_value(const GenerateEF &EF) { return hash_value(EF.Facts); } }; diff --git a/lib/PhasarLLVM/HelperAnalyses.cpp b/lib/PhasarLLVM/HelperAnalyses.cpp index ce9b2f095f..721a313db3 100644 --- a/lib/PhasarLLVM/HelperAnalyses.cpp +++ b/lib/PhasarLLVM/HelperAnalyses.cpp @@ -3,6 +3,7 @@ #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include @@ -10,10 +11,10 @@ namespace psr { HelperAnalyses::HelperAnalyses(std::string IRFile, - std::optional PrecomputedPTS, + std::optional PrecomputedPTS, AliasAnalysisType PTATy, bool AllowLazyPTS, std::vector EntryPoints, - std::optional PrecomputedCG, + std::optional PrecomputedCG, CallGraphAnalysisType CGTy, Soundness SoundnessLevel, bool AutoGlobalSupport) noexcept diff --git a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp index 9b5befda55..c18afd258b 100644 --- a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp +++ b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp @@ -328,6 +328,8 @@ llvm::raw_ostream &psr::operator<<(llvm::raw_ostream &OS, << AlignNum("Debug Intrinsics", Statistics.DebugIntrinsics) << AlignNum("Switches", Statistics.Switches) << AlignNum("GetElementPtrs", Statistics.GetElementPtrs) + << AlignNum("Loads", Statistics.LoadInstructions) + << AlignNum("Stores", Statistics.StoreInstructions) << AlignNum("Phi Nodes", Statistics.PhiNodes) << AlignNum("LandingPads", Statistics.LandingPads) << AlignNum("Basic Blocks", Statistics.BasicBlocks) diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index 7b216842c2..d66b161618 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -97,7 +97,7 @@ LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, bool UseLazyEvaluation, } LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedPTS) + const LLVMAliasSetData &SerializedPTS) : PTA(AliasAnalysisView::create(*IRDB, true, AliasAnalysisType::Basic)) { assert(IRDB != nullptr); // Assume, we already have validated the json schema @@ -105,23 +105,19 @@ LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMAliasSet", "Load precomputed points-to info from JSON"); - const auto &Sets = SerializedPTS.at("AliasSets"); - assert(Sets.is_array()); - const auto &Fns = SerializedPTS.at("AnalyzedFunctions"); - assert(Fns.is_array()); + const auto &Sets = SerializedPTS.AliasSets; + const auto &Fns = SerializedPTS.AnalyzedFunctions; /// Deserialize the AliasSets - an array of arrays (both are to be /// interpreted as sets of metadata-ids) Owner.reserve(Sets.size()); for (const auto &PtsJson : Sets) { - assert(PtsJson.is_array()); auto PTS = Owner.acquire(); for (const auto &Alias : PtsJson) { - const auto AliasStr = Alias.get(); - const auto *Inst = fromMetaDataId(*IRDB, AliasStr); + const auto *Inst = fromMetaDataId(*IRDB, Alias); if (!Inst) { - PHASAR_LOG_LEVEL(WARNING, "Invalid Value-Id: " << AliasStr); + PHASAR_LOG_LEVEL(WARNING, "Invalid Value-Id: " << Alias); continue; } @@ -135,12 +131,7 @@ LLVMAliasSet::LLVMAliasSet(LLVMProjectIRDB *IRDB, AnalyzedFunctions.reserve(Fns.size()); for (const auto &F : Fns) { - if (!F.is_string()) { - PHASAR_LOG_LEVEL(WARNING, "Invalid Function Name: " << F); - continue; - } - - const auto *IRFn = IRDB->getFunction(F.get()); + const auto *IRFn = IRDB->getFunction(F); if (!IRFn) { PHASAR_LOG_LEVEL(WARNING, "Function: " << F << " not in the IRDB"); diff --git a/lib/Utils/NlohmannLogging.cpp b/lib/Utils/NlohmannLogging.cpp index e84d97c44a..c145e9df5f 100644 --- a/lib/Utils/NlohmannLogging.cpp +++ b/lib/Utils/NlohmannLogging.cpp @@ -43,7 +43,7 @@ class LLVMOutputAdapter { llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const nlohmann::json &J) { // do the actual serialization - nlohmann::detail::serializer S(LLVMOutputAdapter(OS), ' '); + nlohmann::detail::serializer S{LLVMOutputAdapter(OS), ' '}; S.dump(J, true, false, 4); return OS; } diff --git a/tools/phasar-cli/phasar-cli.cpp b/tools/phasar-cli/phasar-cli.cpp index c3addbb994..0169441aae 100644 --- a/tools/phasar-cli/phasar-cli.cpp +++ b/tools/phasar-cli/phasar-cli.cpp @@ -10,8 +10,10 @@ #include "phasar/AnalysisStrategy/Strategies.h" #include "phasar/Config/Configuration.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/HelperAnalyses.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" #include "phasar/Pointer/AliasAnalysisType.h" #include "phasar/Utils/IO.h" @@ -442,16 +444,16 @@ int main(int Argc, const char **Argv) { SolverConfig.setComputePersistedSummaries(PersistedSummariesOpt); SolverConfig.setEmitESG(EmitESGAsDotOpt); - std::optional PrecomputedAliasSet; + std::optional PrecomputedAliasSet; if (!LoadPTAFromJsonOpt.empty()) { PHASAR_LOG_LEVEL(INFO, "Load AliasInfo from file: " << LoadCGFromJsonOpt); - PrecomputedAliasSet = readJsonFile(LoadPTAFromJsonOpt); + PrecomputedAliasSet = LLVMAliasSetData::deserializeJson(LoadPTAFromJsonOpt); } - std::optional PrecomputedCallGraph; + std::optional PrecomputedCallGraph; if (!LoadCGFromJsonOpt.empty()) { PHASAR_LOG_LEVEL(INFO, "Load CallGraph from file: " << LoadCGFromJsonOpt); - PrecomputedCallGraph = readJsonFile(LoadCGFromJsonOpt); + PrecomputedCallGraph = CallGraphData::deserializeJson(LoadCGFromJsonOpt); } if (EntryOpt.empty()) { diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp index 619617ccd0..4edb035297 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/DefaultFlowFunctionTest.cpp @@ -37,6 +37,10 @@ class IDEAliasImpl : public DefaultAliasAwareIFDSProblem { class IDENoAliasImpl : public DefaultNoAliasIFDSProblem { public: + using typename DefaultNoAliasIFDSProblem::d_t; + using typename DefaultNoAliasIFDSProblem::l_t; + using typename DefaultNoAliasIFDSProblem::n_t; + IDENoAliasImpl(LLVMProjectIRDB *IRDB) : DefaultNoAliasIFDSProblem(IRDB, {}, {}) {}; diff --git a/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp index 7d55448ea8..496aeff6b1 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp @@ -3,6 +3,7 @@ #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Passes/ValueAnnotationPass.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include "phasar/Utils/Logger.h" @@ -87,7 +88,8 @@ static void analyze(llvm::StringRef File, const GroundTruthTy &Gt, nlohmann::json PrintAsJsonSer = nlohmann::json::parse(SerString); checkSer(PrintAsJsonSer, Gt); - LLVMAliasSet PrintAsJsonDeser(&IRDB, PrintAsJsonSer); + LLVMAliasSet PrintAsJsonDeser(&IRDB, + LLVMAliasSetData::loadJsonString(SerString)); checkDeser(*IRDB.getModule(), PTS, PrintAsJsonDeser); } From 201c9967797a46dc6044d38669d264d448a6986e Mon Sep 17 00:00:00 2001 From: mxHuber Date: Wed, 15 Oct 2025 19:38:41 +0200 Subject: [PATCH 10/16] backup for rebase --- CMakeLists.txt | 73 ++++--------------- cmake/phasar_macros.cmake | 11 --- .../PhasarLLVM/TypeHierarchy/CMakeLists.txt | 1 - 3 files changed, 15 insertions(+), 70 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4b14f22224..dd6baf1873 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -575,65 +575,22 @@ endif() # How does nlohmann json execute the test files to get the profraw?! # -> Apparently coverall does all the work or something? -if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - message("TEST1") - message("build type: ${CMAKE_BUILD_TYPE}") - message("unittests: ${PhasarUnitTests}") - message("TEST2") - add_custom_target(generate_coverage_data - COMMAND ninja - - set(cov_cmd "llvm-profdata-20") - set(cov_mode "merge") - set(cov_arg "-sparse") - set(cov_out "-o \"$filename\".profdata") - execute_process(cov_cmd cov_mode cov_arg "default.profraw" cov_out) - # COMMAND llvm-profdata-20 merge -sparse default.profraw -o "$filename".profdata - - COMMENT "Compile with coverage flags and collect coverage data" - ) - # find_package(Doxygen REQUIRED) - - # set(DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in) - # set(DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) - # configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) - - # add_custom_target(doc_doxygen ALL - # COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} - # WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - # COMMENT "Generating API documentation with Doxygen" - # VERBATIM - # ) - - # nlohmann json approach - - ############################################################################### # Coverage. ############################################################################### -# add_custom_target(ci_test_coverage -# COMMAND CXX=g++ ${CMAKE_COMMAND} -# -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" -# -DJSON_BuildTests=ON -# -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage -# COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage -# COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure -# -# COMMAND CXX=g++ ${CMAKE_COMMAND} -# -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="-m32;--coverage;-fprofile-arcs;-ftest-coverage" -# -DJSON_BuildTests=ON -DJSON_32bitTest=ONLY -# -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage32 -# COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage32 -# COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage32 && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure -# -# COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused -# COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused -# COMMAND ${CMAKE_SOURCE_DIR}/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept -# COMMAND genhtml --title "JSON for Modern C++" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept -# -# COMMENT "Compile and test with coverage" -# ) - - -endif() +find_program(LCOV_TOOL NAMES lcov) +find_program(GENHTML_TOOL NAMES genhtml) + +add_custom_target(ci_test_coverage + COMMAND CXX=clang ${CMAKE_COMMAND} + -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" + -DJSON_BuildTests=ON + -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage + COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure + COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused + COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused + COMMAND ${CMAKE_SOURCE_DIR}/external/json/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept + COMMAND genhtml --title "Coverage_data" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept +) diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 050251dc3b..2bd952852c 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -365,14 +365,3 @@ macro(subdirlist result curdir) set(${result} ${dirlist}) endmacro(subdirlist) - -# TODO: remove this. It makes no sense, since the tests only exist after compiling. -function(run_test_for_prowraw test_name) - message("Run test to generate .prowraw coverage data for test ${test_name}") - - get_filename_component(test ${test_name} NAME_WE) - - message("${CMAKE_CURRENT_SOURCE_DIR}") - execute_process(COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/./${test_name}") - -endfunction() diff --git a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt index 7dd9e1b204..75f52a91d5 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt +++ b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt @@ -7,5 +7,4 @@ set(PointerSources foreach(TEST_SRC ${PointerSources}) add_phasar_unittest(${TEST_SRC}) - run_test_for_prowraw(${TEST_SRC}) endforeach(TEST_SRC) From d77726888b346c518d249e0215cc882254a9cec5 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Fri, 17 Oct 2025 15:03:05 +0200 Subject: [PATCH 11/16] cmake works, g++ fails --- CMakeLists.txt | 56 +++++++++++++++++++++++++++++++++----------------- external/json | 2 +- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dd6baf1873..86b2215a0f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -566,31 +566,49 @@ if(PHASAR_BUILD_DOC) ) endif() -# Generate code coverage -# TODO: code coverage will automatically be enabled when in debug mode. -# Consider creating a seperate variable for coverage generation. -# IIrc debug mode is needed though for coverage generation, double check this. - -# TODO: code below won't work probably. -# How does nlohmann json execute the test files to get the profraw?! -# -> Apparently coverall does all the work or something? - -############################################################################### -# Coverage. -############################################################################### - find_program(LCOV_TOOL NAMES lcov) find_program(GENHTML_TOOL NAMES genhtml) +execute_process(COMMAND ${LCOV_TOOL} --version + OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) +message(STATUS "${LLVM_COV_VERSION_CALL_OUTPUT}") +execute_process(COMMAND ${GENHTML_TOOL} --version + OUTPUT_VARIABLE GENHTML_VERSION_CALL_OUTPUT) +message(STATUS "${GENHTML_VERSION_CALL_OUTPUT}") + +# TODO: add install lcov to bootstrap or something +# Do NOT do: sudo apt-get install -y lcov +# Apparently this version is insanely old and we need a newer version/the newest version +# Installing lcov via the github page + make install has four billion dependencies, so +# we need to install those too: +# These perl packages include: +# +# - Capture::Tiny +# - DateTime +# - Devel::Cover +# - Digest::MD5 +# - File::Spec +# - at least one flavor of JSON module. +# In order of performance/preference: +# - JSON::XS +# - Cpanel::JSON::XS +# - JSON::PP +# - JSON +# - Memory::Process +# - Module::Load::Conditional +# - Scalar::Util +# - Time::HiRes +# - TimeDate + add_custom_target(ci_test_coverage - COMMAND CXX=clang ${CMAKE_COMMAND} + COMMAND CXX=clang++ ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" - -DJSON_BuildTests=ON -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure - COMMAND ${LCOV_TOOL} --directory . --capture --output-file json.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors mismatch --ignore-errors unused - COMMAND ${LCOV_TOOL} -e json.info ${SRC_FILES} --output-file json.info.filtered --rc branch_coverage=1 --ignore-errors unused - COMMAND ${CMAKE_SOURCE_DIR}/external/json/tests/thirdparty/imapdl/filterbr.py json.info.filtered > json.info.filtered.noexcept - COMMAND genhtml --title "Coverage_data" --legend --demangle-cpp --output-directory html --show-details --branch-coverage json.info.filtered.noexcept + COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors empty --ignore-errors mismatch --ignore-errors unused + # COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 + COMMAND ${LCOV_TOOL} -e cov.info ${SRC_FILES} --output-file cov.info.filtered --rc branch_coverage=1 --ignore-errors unused + COMMAND ${CMAKE_SOURCE_DIR}/external/json/tests/thirdparty/imapdl/filterbr.py cov.info.filtered > cov.info.filtered.noexcept + COMMAND genhtml --title "Coverage_data" --legend --demangle-cpp --output-directory html --show-details --branch-coverage cov.info.filtered.noexcept ) diff --git a/external/json b/external/json index 55f93686c0..9cca280a4d 160000 --- a/external/json +++ b/external/json @@ -1 +1 @@ -Subproject commit 55f93686c01528224f448c19128836e7df245f72 +Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 From a8cd3723854f33856d0d2a7a234892c9db786b7d Mon Sep 17 00:00:00 2001 From: mxHuber Date: Tue, 21 Oct 2025 10:36:15 +0200 Subject: [PATCH 12/16] working version (with local hacks -20) --- .gitmodules | 3 ++ CMakeLists.txt | 70 ++++++++++++++++++++++++++++++++++----- cmake/phasar_macros.cmake | 2 ++ 3 files changed, 67 insertions(+), 8 deletions(-) diff --git a/.gitmodules b/.gitmodules index 350885ac54..75f7f0b37b 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/cmake-scripts"] + path = external/cmake-scripts + url = https://github.com/StableCoder/cmake-scripts.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 86b2215a0f..2818928d1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE) # https://reviews.llvm.org/D157613 string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe") -string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer -fprofile-instr-generate -fcoverage-mapping -fprofile-arcs -ftest-coverage") +string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer --coverage -fprofile-arcs -ftest-coverage") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer") string(APPEND CMAKE_CXX_FLAGS_RELEASE "") @@ -270,11 +270,62 @@ find_package(Boost 1.65.1 COMPONENTS graph REQUIRED CONFIG) set(CMAKE_CXX_CLANG_TIDY "") # Nlohmann JSON - include(add_nlohmann_json) add_nlohmann_json() add_json_schema_validator() +# StableCoder cmake-scripts +# TODO: ask fabian if not release or just equal debug is better +if (NOT CMAKE_BUILD_TYPE STREQUAL "Release") + set(CODE_COVERAGE ON) + include(external/cmake-scripts/code-coverage.cmake) + add_code_coverage_all_targets() + # Attempt #1 + # Just the above. Didn't produce any code coverage report. + + # Attempt #2 + # target_code_coverage(${CMAKE_CTEST_COMMAND}) + # + # CMake Error: /home/max/Desktop/dev/Arbeit/phasar-clones/phasar-f-BetterCoverage/build/ccov-data/objects/*: no such file or directory (ignoring) + # ninja: build stopped: subcommand failed. + + # Attempt #3 + # https://stackoverflow.com/questions/60211516/programmatically-get-all-targets-in-a-cmake-project + # + # CMake Error: /home/max/Desktop/dev/Arbeit/phasar-clones/phasar-f-BetterCoverage/build/ccov-data/objects/*: no such file or directory (ignoring) + # ninja: build stopped: subcommand failed. + # set(all_sub_dirs) + # file(GLOB_RECURSE dirs RELATIVE ${PROJECT_SOURCE_DIR} LIST_DIRECTORIES ON ${PROJECT_SOURCE_DIR}/*) + # foreach(dir ${dirs}) + # if(IS_DIRECTORY ${dir}) + # list(APPEND ${all_sub_dirs} ${dir}) + # endif() + # endforeach() +# + # # + # # Get all targets defined at the specified directory (level). + # # _result : The variable in which to store the resulting list of targets. + # # _dir : The directory to query for targets. + # # + # macro(get_targets_by_directory _result _dir) + # get_property(_target DIRECTORY ${_dir} PROPERTY BUILDSYSTEM_TARGETS) + # set(_result ${_target}) + # endmacro() +# + # # + # # Get all targets defined below the specified root directory. + # # _result : The variable in which to store the resulting list of targets. + # # _root_dir : The root project root directory + # # + # foreach(_dir ${all_sub_dirs}) + # get_targets_by_directory(_target ${_dir}) + # if(_target) + # target_code_coverage(${_target}) + # endif() + # endforeach() + +endif() + # Googletest if (NOT PHASAR_IN_TREE) if(PHASAR_BUILD_UNITTESTS AND NOT TARGET gtest) @@ -600,15 +651,18 @@ message(STATUS "${GENHTML_VERSION_CALL_OUTPUT}") # - Time::HiRes # - TimeDate +# number of parallel jobs for CTest +set(N 10) + add_custom_target(ci_test_coverage - COMMAND CXX=clang++ ${CMAKE_COMMAND} + COMMAND CXX=g++ ${CMAKE_COMMAND} -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" - -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/build_coverage - COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/build_coverage - COMMAND cd ${PROJECT_BINARY_DIR}/build_coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure - COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors empty --ignore-errors mismatch --ignore-errors unused + -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/coverage + COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/coverage + COMMAND cd ${PROJECT_BINARY_DIR}/coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure + COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors version --ignore-errors empty,empty --ignore-errors mismatch --ignore-errors unused # COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 - COMMAND ${LCOV_TOOL} -e cov.info ${SRC_FILES} --output-file cov.info.filtered --rc branch_coverage=1 --ignore-errors unused + COMMAND ${LCOV_TOOL} -e cov.info ${SRC_FILES} --output-file cov.info.filtered --rc branch_coverage=1 --ignore-errors version --ignore-errors empty,empty --ignore-errors mismatch --ignore-errors unused COMMAND ${CMAKE_SOURCE_DIR}/external/json/tests/thirdparty/imapdl/filterbr.py cov.info.filtered > cov.info.filtered.noexcept COMMAND genhtml --title "Coverage_data" --legend --demangle-cpp --output-directory html --show-details --branch-coverage cov.info.filtered.noexcept ) diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 2bd952852c..7675e1c9bb 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -35,6 +35,8 @@ function(add_phasar_unittest test_name) ) set_tests_properties("${test}" PROPERTIES LABELS "all") set(CTEST_OUTPUT_ON_FAILURE ON) + + target_code_coverage(${test} AUTO ALL) endfunction() function(validate_binary_version result item) From c30383d28178fee61fa5d9a2d3ff1078f8322e9b Mon Sep 17 00:00:00 2001 From: mxHuber Date: Wed, 22 Oct 2025 18:39:44 +0200 Subject: [PATCH 13/16] added regex for filter, it is not working --- CMakeLists.txt | 78 ++++++++++++++------------------------- cmake/phasar_macros.cmake | 5 ++- 2 files changed, 31 insertions(+), 52 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2818928d1f..cdc2aee6a8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -276,10 +276,36 @@ add_json_schema_validator() # StableCoder cmake-scripts # TODO: ask fabian if not release or just equal debug is better + +# TODO: add install lcov to bootstrap or something +# Do NOT do: sudo apt-get install -y lcov +# Apparently this version is insanely old and we need a newer version/the newest version +# Installing lcov via the github page + make install has four billion dependencies, so +# we need to install those too: +# These perl packages include: +# +# - Capture::Tiny +# - DateTime +# - Devel::Cover +# - Digest::MD5 +# - File::Spec +# - at least one flavor of JSON module. +# In order of performance/preference: +# - JSON::XS +# - Cpanel::JSON::XS +# - JSON::PP +# - JSON +# - Memory::Process +# - Module::Load::Conditional +# - Scalar::Util +# - Time::HiRes +# - TimeDate + if (NOT CMAKE_BUILD_TYPE STREQUAL "Release") set(CODE_COVERAGE ON) include(external/cmake-scripts/code-coverage.cmake) - add_code_coverage_all_targets() + # See https://github.com/StableCoder/cmake-scripts/issues/26 + add_code_coverage_all_targets(EXCLUDE ${COV_FILEPATH_FILTER}) # Attempt #1 # Just the above. Didn't produce any code coverage report. @@ -616,53 +642,3 @@ if(PHASAR_BUILD_DOC) VERBATIM ) endif() - -find_program(LCOV_TOOL NAMES lcov) -find_program(GENHTML_TOOL NAMES genhtml) - -execute_process(COMMAND ${LCOV_TOOL} --version - OUTPUT_VARIABLE LLVM_COV_VERSION_CALL_OUTPUT) -message(STATUS "${LLVM_COV_VERSION_CALL_OUTPUT}") -execute_process(COMMAND ${GENHTML_TOOL} --version - OUTPUT_VARIABLE GENHTML_VERSION_CALL_OUTPUT) -message(STATUS "${GENHTML_VERSION_CALL_OUTPUT}") - -# TODO: add install lcov to bootstrap or something -# Do NOT do: sudo apt-get install -y lcov -# Apparently this version is insanely old and we need a newer version/the newest version -# Installing lcov via the github page + make install has four billion dependencies, so -# we need to install those too: -# These perl packages include: -# -# - Capture::Tiny -# - DateTime -# - Devel::Cover -# - Digest::MD5 -# - File::Spec -# - at least one flavor of JSON module. -# In order of performance/preference: -# - JSON::XS -# - Cpanel::JSON::XS -# - JSON::PP -# - JSON -# - Memory::Process -# - Module::Load::Conditional -# - Scalar::Util -# - Time::HiRes -# - TimeDate - -# number of parallel jobs for CTest -set(N 10) - -add_custom_target(ci_test_coverage - COMMAND CXX=g++ ${CMAKE_COMMAND} - -DCMAKE_BUILD_TYPE=Debug -GNinja -DCMAKE_CXX_FLAGS="--coverage;-fprofile-arcs;-ftest-coverage" - -S${PROJECT_SOURCE_DIR} -B${PROJECT_BINARY_DIR}/coverage - COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/coverage - COMMAND cd ${PROJECT_BINARY_DIR}/coverage && ${CMAKE_CTEST_COMMAND} --parallel ${N} --output-on-failure - COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 --ignore-errors version --ignore-errors empty,empty --ignore-errors mismatch --ignore-errors unused - # COMMAND ${LCOV_TOOL} --directory . --capture --output-file cov.info --rc branch_coverage=1 --rc geninfo_unexecuted_blocks=1 - COMMAND ${LCOV_TOOL} -e cov.info ${SRC_FILES} --output-file cov.info.filtered --rc branch_coverage=1 --ignore-errors version --ignore-errors empty,empty --ignore-errors mismatch --ignore-errors unused - COMMAND ${CMAKE_SOURCE_DIR}/external/json/tests/thirdparty/imapdl/filterbr.py cov.info.filtered > cov.info.filtered.noexcept - COMMAND genhtml --title "Coverage_data" --legend --demangle-cpp --output-directory html --show-details --branch-coverage cov.info.filtered.noexcept -) diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 7675e1c9bb..9d40c86477 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -36,7 +36,10 @@ function(add_phasar_unittest test_name) set_tests_properties("${test}" PROPERTIES LABELS "all") set(CTEST_OUTPUT_ON_FAILURE ON) - target_code_coverage(${test} AUTO ALL) + # set(COV_FILEPATH_FILTER "^.*usr/local/.*$$") + set(COV_FILEPATH_FILTER usr/local/*) + + target_code_coverage(${test} AUTO ALL EXCLUDE ${COV_FILEPATH_FILTER} LLVM_EXCLUDE ${COV_FILEPATH_FILTER} LCOV_EXCLUDE ${COV_FILEPATH_FILTER}) endfunction() function(validate_binary_version result item) From 630662b4112df2d22040a1efcbaf8c04f9cda6d2 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Thu, 23 Oct 2025 01:00:56 +0200 Subject: [PATCH 14/16] fixed filtering + cleanup --- CMakeLists.txt | 47 ++------------------------------------- cmake/phasar_macros.cmake | 5 +---- 2 files changed, 3 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cdc2aee6a8..1e7a2beb5a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,52 +304,9 @@ add_json_schema_validator() if (NOT CMAKE_BUILD_TYPE STREQUAL "Release") set(CODE_COVERAGE ON) include(external/cmake-scripts/code-coverage.cmake) - # See https://github.com/StableCoder/cmake-scripts/issues/26 - add_code_coverage_all_targets(EXCLUDE ${COV_FILEPATH_FILTER}) - # Attempt #1 - # Just the above. Didn't produce any code coverage report. - - # Attempt #2 - # target_code_coverage(${CMAKE_CTEST_COMMAND}) - # - # CMake Error: /home/max/Desktop/dev/Arbeit/phasar-clones/phasar-f-BetterCoverage/build/ccov-data/objects/*: no such file or directory (ignoring) - # ninja: build stopped: subcommand failed. - - # Attempt #3 - # https://stackoverflow.com/questions/60211516/programmatically-get-all-targets-in-a-cmake-project - # - # CMake Error: /home/max/Desktop/dev/Arbeit/phasar-clones/phasar-f-BetterCoverage/build/ccov-data/objects/*: no such file or directory (ignoring) - # ninja: build stopped: subcommand failed. - # set(all_sub_dirs) - # file(GLOB_RECURSE dirs RELATIVE ${PROJECT_SOURCE_DIR} LIST_DIRECTORIES ON ${PROJECT_SOURCE_DIR}/*) - # foreach(dir ${dirs}) - # if(IS_DIRECTORY ${dir}) - # list(APPEND ${all_sub_dirs} ${dir}) - # endif() - # endforeach() -# - # # - # # Get all targets defined at the specified directory (level). - # # _result : The variable in which to store the resulting list of targets. - # # _dir : The directory to query for targets. - # # - # macro(get_targets_by_directory _result _dir) - # get_property(_target DIRECTORY ${_dir} PROPERTY BUILDSYSTEM_TARGETS) - # set(_result ${_target}) - # endmacro() -# - # # - # # Get all targets defined below the specified root directory. - # # _result : The variable in which to store the resulting list of targets. - # # _root_dir : The root project root directory - # # - # foreach(_dir ${all_sub_dirs}) - # get_targets_by_directory(_target ${_dir}) - # if(_target) - # target_code_coverage(${_target}) - # endif() - # endforeach() + set(COV_FILEPATH_FILTER "(usr/local/*|external/)") + add_code_coverage_all_targets(EXCLUDE ${COV_FILEPATH_FILTER} LLVM_EXCLUDE ${COV_FILEPATH_FILTER} LCOV_EXCLUDE ${COV_FILEPATH_FILTER}) endif() # Googletest diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 9d40c86477..7675e1c9bb 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -36,10 +36,7 @@ function(add_phasar_unittest test_name) set_tests_properties("${test}" PROPERTIES LABELS "all") set(CTEST_OUTPUT_ON_FAILURE ON) - # set(COV_FILEPATH_FILTER "^.*usr/local/.*$$") - set(COV_FILEPATH_FILTER usr/local/*) - - target_code_coverage(${test} AUTO ALL EXCLUDE ${COV_FILEPATH_FILTER} LLVM_EXCLUDE ${COV_FILEPATH_FILTER} LCOV_EXCLUDE ${COV_FILEPATH_FILTER}) + target_code_coverage(${test} AUTO ALL) endfunction() function(validate_binary_version result item) From 1f683ea7b4534d30d23315d46fe90412bf75cb74 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Thu, 23 Oct 2025 23:19:38 +0200 Subject: [PATCH 15/16] basic ci impl, lib coverage, fixed release cmake error --- .github/workflows/ci.yml | 9 ++++----- CMakeLists.txt | 2 +- cmake/phasar_macros.cmake | 8 +++++++- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ed8eda4e6..6511ad45dd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,8 +77,7 @@ jobs: cmake -S . -B build -Dphasar_ROOT=../../INSTALL cmake --build ./build --target run_sample_programs - - name: Check Test Coverage and generate HTML report - uses: threeal/gcovr-action@v1.1.0 - with: - gcov-executable: llvm-cov gcov - html-out: coverage.html + - name: Check test coverage and generate HTML report + shell: bash + run: | + cmake --build ./build --target ccov-all diff --git a/CMakeLists.txt b/CMakeLists.txt index 1e7a2beb5a..1abb5e429d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -72,7 +72,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE) # https://reviews.llvm.org/D157613 string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe") -string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer --coverage -fprofile-arcs -ftest-coverage") +string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer -fprofile-arcs -ftest-coverage") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer") string(APPEND CMAKE_CXX_FLAGS_RELEASE "") diff --git a/cmake/phasar_macros.cmake b/cmake/phasar_macros.cmake index 7675e1c9bb..a7a09b126f 100644 --- a/cmake/phasar_macros.cmake +++ b/cmake/phasar_macros.cmake @@ -36,7 +36,9 @@ function(add_phasar_unittest test_name) set_tests_properties("${test}" PROPERTIES LABELS "all") set(CTEST_OUTPUT_ON_FAILURE ON) - target_code_coverage(${test} AUTO ALL) + if (NOT CMAKE_BUILD_TYPE STREQUAL "Release") + target_code_coverage(${test} AUTO ALL) + endif() endfunction() function(validate_binary_version result item) @@ -353,6 +355,10 @@ function(add_phasar_library name) endif() set_property(GLOBAL APPEND PROPERTY LLVM_EXPORTS ${name}) + + if (NOT CMAKE_BUILD_TYPE STREQUAL "Release") + target_code_coverage(${name} AUTO ALL) + endif() endfunction(add_phasar_library) macro(subdirlist result curdir) From fd48ffb86addb44f111a14a44d90ec9eb7aa8ef8 Mon Sep 17 00:00:00 2001 From: mxHuber Date: Thu, 23 Oct 2025 23:42:51 +0200 Subject: [PATCH 16/16] added cmake-scripts to submodule index --- external/cmake-scripts | 1 + 1 file changed, 1 insertion(+) create mode 160000 external/cmake-scripts diff --git a/external/cmake-scripts b/external/cmake-scripts new file mode 160000 index 0000000000..b9add21937 --- /dev/null +++ b/external/cmake-scripts @@ -0,0 +1 @@ +Subproject commit b9add21937b744ead790bf381e760b3624a95ad4