From d035ca5e190e913c91bbc4bd31f3b5caca6b8b35 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 10:09:55 +0200 Subject: [PATCH 1/6] Pull-in the WPDSSolver (TODO: value computation) --- .../phasar/DataFlow/WPDS/IDERuleProvider.h | 179 ++++++++++ include/phasar/DataFlow/WPDS/RuleProvider.h | 61 ++++ .../phasar/DataFlow/WPDS/Solver/WPDSSolver.h | 335 ++++++++++++++++++ .../DataFlow/WPDS/Solver/WPDSSolverResults.h | 285 +++++++++++++++ include/phasar/Utils/Compressor.h | 2 +- include/phasar/Utils/SemiRing.h | 44 +++ .../DataFlow/IfdsIde/CMakeLists.txt | 1 + .../DataFlow/IfdsIde/WPDSSolverTest.cpp | 186 ++++++++++ 8 files changed, 1092 insertions(+), 1 deletion(-) create mode 100644 include/phasar/DataFlow/WPDS/IDERuleProvider.h create mode 100644 include/phasar/DataFlow/WPDS/RuleProvider.h create mode 100644 include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h create mode 100644 include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h create mode 100644 unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp diff --git a/include/phasar/DataFlow/WPDS/IDERuleProvider.h b/include/phasar/DataFlow/WPDS/IDERuleProvider.h new file mode 100644 index 0000000000..0315af1b3c --- /dev/null +++ b/include/phasar/DataFlow/WPDS/IDERuleProvider.h @@ -0,0 +1,179 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#include "phasar/ControlFlow/SparseCFGProvider.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" +#include "phasar/DataFlow/WPDS/RuleProvider.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/SmallVector.h" + +namespace psr::wpds { +template class IDERuleProvider { +public: + using control_location_type = typename ProblemT::d_t; + using stack_element_type = typename ProblemT::n_t; + using weight_type = typename ProblemT::EdgeFunctionType; + + static constexpr llvm::StringLiteral LogCategory = "IDERuleProvider"; + + IDERuleProvider(ProblemT *Problem, const ICFGTy *ICF) noexcept + : Problem(&assertNotNull(Problem)), ICF(&assertNotNull(ICF)) { + static_assert(RuleProvider); + } + + [[nodiscard]] auto getNormalRules(ByConstRef CL, + ByConstRef SE) { + + llvm::SmallVector< + std::tuple> + Outs; + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getNormalRules]: CL=" << DToString(CL) + << "; SE=" << NToString(SE)); + + if (ICF->isCallSite(SE)) { + auto Callees = ICF->getCalleesOfCallAt(SE); + for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { + auto Facts = FECache.getCallToRetFlowFunction(SE, Succ, Callees) + ->computeTargets(CL); + for (auto &Fct : Facts) { + auto W = FECache.getCallToRetEdgeFunction(SE, CL, Succ, Fct, Callees); + Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + } + } + + for (const auto &DestFun : Callees) { + if (auto SumFF = FECache.getSummaryFlowFunction(SE, DestFun)) { + auto Facts = SumFF->computeTargets(CL); + for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { + for (auto &Fct : Facts) { + auto W = FECache.getSummaryEdgeFunction(SE, CL, Succ, Fct); + Outs.emplace_back(Fct, Succ, std::move(W)); + } + } + } + } + + } else { + for (const auto &Succ : ICF->getSuccsOf(SE)) { + auto Facts = + FECache.getNormalFlowFunction(SE, Succ)->computeTargets(CL); + for (auto &Fct : Facts) { + auto W = FECache.getNormalEdgeFunction(SE, CL, Succ, Fct); + Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + } + } + } + + return Outs; + } + + [[nodiscard]] auto getPushRules(ByConstRef CL, + ByConstRef SE) { + llvm::SmallVector> + Outs; + if (!ICF->isCallSite(SE)) { + return Outs; + } + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getPushRules]: CL=" << DToString(CL) + << "; SE=" << NToString(SE)); + + auto Callees = ICF->getCalleesOfCallAt(SE); + for (const auto &DestFun : Callees) { + if (FECache.getSummaryFlowFunction(SE, DestFun)) { + // Handled in getNormalRules() + continue; + } + + auto Facts = FECache.getCallFlowFunction(SE, DestFun)->computeTargets(CL); + for (const auto &EntrySE : ICF->getStartPointsOf(DestFun)) { + for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { + for (auto &&Fct : Facts) { + auto W = FECache.getCallEdgeFunction(SE, CL, DestFun, Fct); + Outs.emplace_back(Fct, Succ, EntrySE, std::move(W)); + } + } + } + } + + return Outs; + } + + [[nodiscard]] bool hasPopRules(ByConstRef /*CL*/, + ByConstRef SE) { + // TODO: Be more precise here, filtering for facts CL that we actually need + // in the summary. + return ICF->isExitInst(SE); + } + + [[nodiscard]] auto getPopRules(ByConstRef CL, + ByConstRef ExitSE, + ByConstRef RetSiteSE, + ByConstRef /*EntrySE*/ + ) { + llvm::SmallVector> Outs; + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getPopRules]: CL=" + << DToString(CL) + << "; ExitSE=" << NToString(ExitSE) + << "; RetSiteSE=" << NToString(RetSiteSE)); + + auto DestFun = ICF->getFunctionOf(ExitSE); + + for (const auto &CS : ICF->getPredsOf(RetSiteSE)) { + if (!ICF->isCallSite(CS)) { + continue; + } + + auto Facts = FECache.getRetFlowFunction(CS, DestFun, ExitSE, RetSiteSE) + ->computeTargets(CL); + for (auto &Fct : Facts) { + auto W = FECache.getReturnEdgeFunction(CS, DestFun, ExitSE, CL, + RetSiteSE, Fct); + Outs.emplace_back(std::move(Fct), std::move(W)); + } + } + + return Outs; + } + + [[nodiscard]] auto initialSeeds() { + llvm::SmallVector> + Outs; + + for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { + for (const auto &[Fact, _] : Facts) { + Outs.emplace_back(Fact, Inst); + } + } + + return Outs; + } + + [[nodiscard]] constexpr auto &ideProblem() const noexcept { return *Problem; } + +private: + ProblemT *Problem{}; + const ICFGTy *ICF{}; + + FlowEdgeFunctionCache FECache{ + *Problem}; +}; +} // namespace psr::wpds diff --git a/include/phasar/DataFlow/WPDS/RuleProvider.h b/include/phasar/DataFlow/WPDS/RuleProvider.h new file mode 100644 index 0000000000..c51c151f0b --- /dev/null +++ b/include/phasar/DataFlow/WPDS/RuleProvider.h @@ -0,0 +1,61 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#include "phasar/Utils/TypeTraits.h" + +#include + +namespace psr::wpds { +template +concept RuleProvider = requires(T &RP, typename T::control_location_type CL, + typename T::stack_element_type SE) { + typename T::control_location_type; + typename T::stack_element_type; + typename T::weight_type; + { + RP.getNormalRules(CL, SE) + } -> psr::is_iterable_over_v< + std::tuple>; + + { + RP.getPushRules(CL, SE) + } -> psr::is_iterable_over_v>; + + { + // CurrCL, ExitSE, RetSiteSE, EntrySE + RP.getPopRules(CL, SE, SE, SE) + } -> psr::is_iterable_over_v< + std::tuple>; + + { RP.hasPopRules(CL, SE) } -> std::convertible_to; + + { + RP.initialSeeds() + } -> psr::is_iterable_over_v>; +}; + +template +concept CanInjectAdditionalPushEdges = + requires(T &RP, typename T::control_location_type CL, + typename T::stack_element_type SE) { + RP.injectAdditionalPushEdges( + CL, SE, + [](llvm::ArrayRef> + Edges, + typename T::weight_type Weight) {}); + }; +} // namespace psr::wpds diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h new file mode 100644 index 0000000000..9e901ec4d4 --- /dev/null +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h @@ -0,0 +1,335 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#include "phasar/DataFlow/WPDS/RuleProvider.h" +#include "phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/NonNullPtr.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/SemiRing.h" +#include "phasar/Utils/TypedVector.h" + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" + +#include +#include + +namespace psr { + +template + requires std::same_as +class WPDSSolver : private wpds::detail::SolverResultsData< + typename RuleProviderT::control_location_type, + typename RuleProviderT::stack_element_type, + typename RuleProviderT::weight_type> { + using base_t = wpds::detail::SolverResultsData< + typename RuleProviderT::control_location_type, + typename RuleProviderT::stack_element_type, + typename RuleProviderT::weight_type>; + + using base_t::ESGNodeCompressor; + using base_t::Incoming; + using base_t::PathEdges; + +public: + using cl_t = typename RuleProviderT::control_location_type; + using se_t = typename RuleProviderT::stack_element_type; + using weight_t = typename RuleProviderT::weight_type; + using path_edge_t = std::tuple; + + static constexpr llvm::StringLiteral LogCategory = "RuleBasedSolver"; + + explicit WPDSSolver(RuleProviderT *RP, SemiRingT *SR) noexcept + : RP(RP), SR(SR) {} + explicit WPDSSolver(NonNullPtr RP, + NonNullPtr SR) noexcept + : RP(RP), SR(SR) {} + WPDSSolver(RuleProviderT *RP, std::nullptr_t SR) = delete; + WPDSSolver(std::nullptr_t RP, SemiRingT *SR) = delete; + WPDSSolver(std::nullptr_t RP, std::nullptr_t SR) = delete; + + wpds::SolverResults solve() & { + solveImpl(); + return getSolverResults(); + } + wpds::OwningSolverResults solve() && { + solveImpl(); + return consumeSolverResults(); + } + + [[nodiscard]] wpds::SolverResults + getSolverResults() const noexcept [[clang::lifetimebound]] { + return {this}; + } + + [[nodiscard]] wpds::OwningSolverResults + consumeSolverResults() const noexcept { + return {std::move(this)}; + } + +private: + void solveImpl() { + submitInitialSeeds(); + + while (!WL.empty()) { + auto [CurrPE, CurrWeight] = WL.pop_back_val(); + auto &&[CurrSrc, CurrTarget, CurrTgtFact] = CurrPE; + + for (const auto &[SuccCL, SuccSE, SuccWeight] : + RP->getNormalRules(CurrTgtFact, CurrTarget)) { + propagate({CurrSrc, SuccSE, SuccCL}, + SR->extend(CurrWeight, SuccWeight)); + } + + for (const auto &[SuccFact, RetSite, EntrySE, PushWeight] : + RP->getPushRules(CurrTgtFact, CurrTarget)) { + + if constexpr (requires() { RP->makeSrcFacts(SuccFact); }) { + for (const auto &RealSuccFact : RP->makeSrcFacts(SuccFact)) { + auto Entry = compressNode(RealSuccFact, EntrySE); + + propagate({Entry, EntrySE, SuccFact}, SR->identity()); + auto IncWeight = SR->extend(CurrWeight, PushWeight); + addIncoming(Entry, CurrSrc, RetSite, IncWeight); + applyEndSummary(Entry, CurrSrc, RetSite, IncWeight); + } + } else { + + auto Entry = compressNode(SuccFact, EntrySE); + + propagate({Entry, EntrySE, SuccFact}, SR->identity()); + auto IncWeight = SR->extend(CurrWeight, PushWeight); + addIncoming(Entry, CurrSrc, RetSite, IncWeight); + applyEndSummary(Entry, CurrSrc, RetSite, IncWeight); + } + } + + if constexpr (wpds::CanInjectAdditionalPushEdges) { + RP->injectAdditionalPushEdges( + CurrTgtFact, CurrTarget, + [&](llvm::ArrayRef> Edges, + weight_t W) { + if (!Edges.empty()) { + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "injectAdditionalPushEdges:"); + PHASAR_LOG_LEVEL_CAT( + DEBUG, LogCategory, + "injectAdditionalPushEdges: > For CurrTgtFact: " + << DToString(CurrTgtFact) << " AT " + << NToString(CurrTarget)); + } + + if constexpr (requires() { + RP->makeSrcFacts(std::get<0>(Edges.front())); + }) { + // TODO: This is extremely ugly, but it works... + const auto &[FirstCL, FirstEntrySE, FirstRetSite] = + Edges.front(); + for (const auto &RealCL : RP->makeSrcFacts(FirstCL)) { + auto TopmostSrc = CurrSrc; + + PHASAR_LOG_LEVEL_CAT( + DEBUG, LogCategory, + " > CL" << DToString(RealCL) + << "; EntrySE: " << NToString(FirstEntrySE) + << "; RetSite: " << NToString(FirstRetSite)); + + auto Entry = compressNode(RealCL, FirstEntrySE); + + propagate({Entry, FirstEntrySE, FirstCL}, SR->identity()); + auto IncWeight = + SR->extend(CurrWeight, std::exchange(W, SR->identity())); + addIncoming(Entry, TopmostSrc, FirstRetSite, IncWeight); + applyEndSummary(Entry, TopmostSrc, FirstRetSite, IncWeight); + + TopmostSrc = Entry; + + for (auto &&[CL, EntrySE, RetSite] : Edges.drop_front()) { + + PHASAR_LOG_LEVEL_CAT( + DEBUG, LogCategory, + " > CL" << DToString(CL) + << "; EntrySE: " << NToString(EntrySE) + << "; RetSite: " << NToString(RetSite)); + + auto Entry = compressNode(CL, EntrySE); + + propagate({Entry, EntrySE, CL}, SR->identity()); + auto IncWeight = SR->extend( + CurrWeight, std::exchange(W, SR->identity())); + addIncoming(Entry, TopmostSrc, RetSite, IncWeight); + applyEndSummary(Entry, TopmostSrc, RetSite, IncWeight); + + TopmostSrc = Entry; + } + } + } else { + auto TopmostSrc = CurrSrc; + for (auto &&[CL, EntrySE, RetSite] : Edges) { + + PHASAR_LOG_LEVEL_CAT( + DEBUG, LogCategory, + " > CL" << DToString(CL) + << "; EntrySE: " << NToString(EntrySE) + << "; RetSite: " << NToString(RetSite)); + + auto Entry = compressNode(CL, EntrySE); + + propagate({Entry, EntrySE, CL}, SR->identity()); + auto IncWeight = + SR->extend(CurrWeight, std::exchange(W, SR->identity())); + addIncoming(Entry, TopmostSrc, RetSite, IncWeight); + applyEndSummary(Entry, TopmostSrc, RetSite, IncWeight); + + TopmostSrc = Entry; + } + } + }); + } + + // else { + + // llvm::errs() << "NO injectAdditionalPushEdges:\n"; + // } + + if (RP->hasPopRules(CurrTgtFact, CurrTarget)) { + + if (!addEndSummary(CurrSrc, CurrTgtFact, CurrTarget, CurrWeight)) { + continue; + } + + const auto &[_, CurrSource] = ESGNodeCompressor[CurrSrc]; + for (const auto &[CSNode, IncWeight] : Incoming[CurrSrc]) { + auto &&[CSSrc, RetSite] = CSNode; + auto SumExtWeight = SR->extend(IncWeight, CurrWeight); + for (const auto &[PopCL, PopWeight] : + RP->getPopRules(CurrTgtFact, CurrTarget, RetSite, CurrSource)) { + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, "Summary Edge:"); + propagate({CSSrc, RetSite, PopCL}, + SR->extend(SumExtWeight, PopWeight)); + } + } + } + } + } + + [[nodiscard]] wpds::ESGNodeId compressNode(cl_t CL, se_t SE) { + auto [Id, Inserted] = + ESGNodeCompressor.insert({std::move(CL), std::move(SE)}); + if (Inserted) { + Incoming.emplace_back(); + EndSummary.emplace_back(); + } + return Id; + } + + void submitInitialSeeds() { + for (const auto &[InitCL, InitSE] : RP->initialSeeds()) { + auto Init = compressNode(InitCL, InitSE); + propagate({Init, InitSE, InitCL}, SR->identity()); + } + } + + void propagate(path_edge_t PE, weight_t Weight) { + auto &&[PESrc, SE, CL] = PE; + + auto [It, Inserted] = + PathEdges[std::pair{CL, SE}].try_emplace(PESrc, Weight); + if (!Inserted) { + Weight = SR->combine(It->second, Weight); + if (It->second != Weight) { + It->second = Weight; + Inserted = true; + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[propagate]: UPDATED (" + << NToString(ESGNodeCompressor[PESrc].second) + << ", " + << DToString(ESGNodeCompressor[PESrc].first) + << ")\t-->\t(" << NToString(SE) << ", " + << DToString(CL) << ") w/ " << It->second); + } + } else { + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[propagate]: NEW (" + << NToString(ESGNodeCompressor[PESrc].second) + << ", " + << DToString(ESGNodeCompressor[PESrc].first) + << ")\t-->\t(" << NToString(SE) << ", " + << DToString(CL) << ") w/ " << It->second); + } + + if (Inserted) { + WL.emplace_back(std::move(PE), std::move(Weight)); + } + } + + void addIncoming(wpds::ESGNodeId PushNod, wpds::ESGNodeId CSSrc, se_t RetSite, + weight_t PushWeight) { + + PHASAR_LOG_LEVEL_CAT( + DEBUG, LogCategory, + "[addIncoming]: PushFact: " + << DToString(ESGNodeCompressor[PushNod].first) + << ", PushSE: " << NToString(ESGNodeCompressor[PushNod].second) + << "; CurrSource: " << NToString(ESGNodeCompressor[CSSrc].second) + << ", CSSrcFact: " << DToString(ESGNodeCompressor[CSSrc].first) + << "; RetSite: " << NToString(RetSite)); + + auto [It, Inserted] = Incoming[PushNod].try_emplace( + std::pair{CSSrc, std::move(RetSite)}, std::move(PushWeight)); + if (!Inserted) { + It->second = SR->combine(std::move(It->second), std::move(PushWeight)); + } + } + + bool addEndSummary(wpds::ESGNodeId Src, cl_t TgtFact, se_t TgtSE, + weight_t TgtWeight) { + auto [It, Inserted] = EndSummary[Src].try_emplace( + std::pair{std::move(TgtFact), std::move(TgtSE)}, std::move(TgtWeight)); + if (!Inserted) { + auto Merged = SR->combine(It->second, TgtWeight); + if (It->second != TgtWeight) { + It->second = std::move(Merged); + Inserted = true; + } + } + return Inserted; + } + + void applyEndSummary(wpds::ESGNodeId PushNod, wpds::ESGNodeId CSSrc, + se_t RetSite, weight_t PushWeight) { + const auto &Sum = EndSummary[PushNod]; + const auto &PushSE = ESGNodeCompressor[PushNod].second; + for (const auto &[SumNode, SumWeight] : Sum) { + auto &&[SumCL, SumSE] = SumNode; + auto SumExtWeight = SR->extend(PushWeight, SumWeight); + for (const auto &[PopCL, PopWeight] : + RP->getPopRules(SumCL, SumSE, RetSite, PushSE)) { + propagate({CSSrc, RetSite, PopCL}, SR->extend(SumExtWeight, PopWeight)); + } + } + } + + NonNullPtr RP{}; + NonNullPtr SR{}; + + llvm::SmallVector> WL; + + TypedVector, weight_t>> + EndSummary; +}; + +} // namespace psr diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h new file mode 100644 index 0000000000..59314fea9d --- /dev/null +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h @@ -0,0 +1,285 @@ +#pragma once + +#include "phasar/Utils/Compressor.h" +#include "phasar/Utils/MapUtils.h" +#include "phasar/Utils/NonNullPtr.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/StrongTypeDef.h" +#include "phasar/Utils/TypedVector.h" + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/STLExtras.h" + +PHASAR_STRONG_TYPEDEF(psr::wpds, uint32_t, ESGNodeId); + +namespace psr::wpds { +namespace detail { + +template +struct SolverResultsData { + using cl_t = CLTy; + using se_t = SETy; + using weight_t = WeightTy; + + Compressor, wpds::ESGNodeId> ESGNodeCompressor; + llvm::DenseMap, + llvm::SmallDenseMap> + PathEdges; + TypedVector, weight_t>> + Incoming; +}; + +template +class SolverResultsBase { +public: + using cl_t = CLTy; + using se_t = SETy; + using weight_t = WeightTy; + + [[nodiscard]] bool holdsFactAt(ByConstRef Fact, + ByConstRef At) const { + return holdsFactAt({Fact, At}); + } + + [[nodiscard]] bool + holdsFactAt(ByConstRef> FactAt) const { + for (const auto &[Src, Weight] : getOrDefault(self().pathEdges(), FactAt)) { + if (self().incoming()[Src].empty()) { + // Directly generated from zero + return true; + } + } + + return false; + } + + [[nodiscard]] bool isInResultSet(ByConstRef Fact, + ByConstRef At) const { + return isInResultSet({Fact, At}); + } + + [[nodiscard]] bool + isInResultSet(ByConstRef> FactAt) const { + return self().pathEdges().count(FactAt); + } + + bool traversePath( + ByConstRef> FactAt, + std::invocable auto WithPATransition) const { + llvm::SmallVector WL; + llvm::DenseSet Seen; + + for (const auto &[Src, Weight] : getOrDefault(self().pathEdges(), FactAt)) { + WL.emplace_back(Src); + Seen.insert(Src); + + const auto &[SrcCL, SrcSE] = self().esgNodeCompressor()[Src]; + std::invoke(WithPATransition, FactAt.first, FactAt.second, SrcCL, SrcSE, + Weight); + } + + if (WL.empty()) { + // Fact does not hold + return false; + } + + do { + auto Curr = WL.pop_back_val(); + const auto &[CurrCL, CurrSE] = self().esgNodeCompressor()[Curr]; + + const auto &Inc = self().incoming()[Curr]; + if (!Inc.empty()) { + for (const auto &[CSNode, Weight] : Inc) { + const auto &[CSSrc, RetSite] = CSNode; + + const auto &[CSSrcCL, CSSrcSE] = self().esgNodeCompressor()[CSSrc]; + std::invoke(WithPATransition, CurrCL, CurrSE, CSSrcCL, CSSrcSE, + Weight); + + if (Seen.insert(CSSrc).second) { + WL.emplace_back(CSSrc); + } + } + } + } while (!WL.empty()); + + return true; + } + + [[nodiscard]] auto getAllIFDSResultEntries() const { + return llvm::make_filter_range(self().esgNodeCompressor(), + [this](const auto &Entry) { + const auto &[CL, SE] = Entry; + return this->isInResultSet(CL, SE); + }); + } + + void dumpPath(ByConstRef Fact, ByConstRef At, + llvm::raw_ostream &OS); + + void dumpResults(llvm::raw_ostream &OS = llvm::outs()); + +private: + constexpr SolverResultsBase() noexcept = default; + friend Derived; + + [[nodiscard]] const auto &self() const noexcept { + return *static_cast(this); + } +}; + +template +void SolverResultsBase::dumpPath( + ByConstRef Fact, ByConstRef At, llvm::raw_ostream &OS) { + OS << "digraph Path {\n"; + + Compressor> CLC; + const auto AddCL = [&](cl_t CL) -> uint32_t { + auto [Id, Inserted] = CLC.insert(std::pair{CL, false}); + Id++; + if (Inserted) { + OS << " " << Id << "[label=\""; + OS.write_escaped(DToString(CL)) << "\"];\n"; + } + return Id; + }; + + const auto AddSrcCL = [&](cl_t CL) -> uint32_t { + auto [Id, Inserted] = CLC.insert(std::pair{CL, true}); + Id++; + if (Inserted) { + OS << " " << Id << "[label=\"q["; + OS.write_escaped(DToString(CL)) << "]\", style=dashed];\n"; + } + return Id; + }; + + traversePath({Fact, At}, + [&](const auto &TgtCL, const auto &TgtSE, const auto &SrcCL, + const auto & /*SrcSE*/, const auto & /*Weight*/) { + auto CurrCLId = AddCL(TgtCL); + auto SrcCLId = AddSrcCL(SrcCL); + + OS << " " << CurrCLId << "->" << SrcCLId << "[label=\""; + OS.write_escaped(NToString(TgtSE)) << "\"];\n"; + }); + + OS << "}\n"; +} + +template +void SolverResultsBase::dumpResults( + llvm::raw_ostream &OS) { + OS << "digraph RuleBasedSolver {\n"; + OS << " rankdir=LR;\n"; + OS << " node [shape=circle];\n"; + OS << " 0[label=\"ACC\", shape=doublecircle];\n"; + + Compressor> CLC; + const auto AddCL = [&](cl_t CL) -> uint32_t { + auto [Id, Inserted] = CLC.insert(std::pair{CL, false}); + Id++; + if (Inserted) { + OS << " " << Id << "[label=\""; + OS.write_escaped(DToString(CL)) << "\"];\n"; + } + return Id; + }; + + const auto AddSrcCL = [&](cl_t CL) -> uint32_t { + auto [Id, Inserted] = CLC.insert(std::pair{CL, true}); + Id++; + if (Inserted) { + OS << " " << Id << "[label=\"q["; + OS.write_escaped(DToString(CL)) << "]\", style=dashed];\n"; + } + return Id; + }; + for (const auto &[PE, Inner] : self().pathEdges()) { + const auto &[TgtCL, TgtSE] = PE; + auto TgtClId = AddCL(TgtCL); + + for (const auto &[Src, Weight] : Inner) { + const auto &[SrcCL, SrcSE] = self().esgNodeCompressor()[Src]; + auto SrcClId = AddSrcCL(SrcCL); + + OS << " " << TgtClId << "->" << SrcClId << "[label=\""; + OS.write_escaped(NToString(TgtSE)) << "\"];\n"; + + if (SrcSE == TgtSE && SrcCL == TgtCL) { + const auto &Inc = self().incoming()[Src]; + if (!Inc.empty()) { + for (const auto &[CSNode, _] : Inc) { + const auto &[CSSrc, RetSite] = CSNode; + const auto &CSSrcFact = self().esgNodeCompressor()[CSSrc].first; + + auto CSClId = AddSrcCL(CSSrcFact); + + OS << " " << SrcClId << "->" << CSClId << "[label=\""; + OS.write_escaped(NToString(SrcSE)) << "\", style=dashed];\n"; + } + } else { + OS << " " << SrcClId << "->0[label=\""; + OS.write_escaped(NToString(SrcSE)) << "\", style=dashed];\n"; + } + continue; + } + } + } + + OS << "}\n"; +} +} // namespace detail + +template +class OwningSolverResults + : public detail::SolverResultsBase< + OwningSolverResults, CLTy, SETy, WeightTy> { + friend detail::SolverResultsBase, + CLTy, SETy, WeightTy>; + +public: + OwningSolverResults(detail::SolverResultsData &&Data) + : Data(std::move(Data)) {} + +private: + [[nodiscard]] const auto &pathEdges() const noexcept { + return Data.PathEdges; + } + [[nodiscard]] const auto &incoming() const noexcept { return Data.Incoming; } + [[nodiscard]] const auto &esgNodeCompressor() const noexcept { + return Data.ESGNodeCompressor; + } + + detail::SolverResultsData Data; +}; + +template +class SolverResults + : public detail::SolverResultsBase, + CLTy, SETy, WeightTy> { + friend detail::SolverResultsBase, CLTy, + SETy, WeightTy>; + +public: + constexpr SolverResults( + NonNullPtr> + Data) noexcept + : Data(Data) {} + +private: + [[nodiscard]] const auto &pathEdges() const noexcept { + return Data->PathEdges; + } + [[nodiscard]] const auto &incoming() const noexcept { return Data->Incoming; } + [[nodiscard]] const auto &esgNodeCompressor() const noexcept { + return Data->ESGNodeCompressor; + } + + NonNullPtr> Data; +}; + +} // namespace psr::wpds diff --git a/include/phasar/Utils/Compressor.h b/include/phasar/Utils/Compressor.h index 6e23750ad3..ed14f676dd 100644 --- a/include/phasar/Utils/Compressor.h +++ b/include/phasar/Utils/Compressor.h @@ -160,7 +160,7 @@ class Compressor { if (auto It = ToInt.find(&Elem); It != ToInt.end()) { return {It->second, false}; } - auto Ret = Id(FromInt.size()); + auto Ret = IdT(FromInt.size()); auto *Ins = &FromInt.emplace_back(std::move(Elem)); ToInt[Ins] = Ret; return {Ret, true}; diff --git a/include/phasar/Utils/SemiRing.h b/include/phasar/Utils/SemiRing.h index e4999d329e..5dba6c6d54 100644 --- a/include/phasar/Utils/SemiRing.h +++ b/include/phasar/Utils/SemiRing.h @@ -11,11 +11,31 @@ #define PHASAR_UTILS_SEMIRING_H #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/Domain/BinaryDomain.h" + +#include namespace psr { + +template +concept IsSemiRing = requires(T &SR, const typename T::EdgeFunctionType &EF) { + typename T::EdgeFunctionType; + requires IsEdgeFunction; + + { SR.extend(EF, EF) } -> std::convertible_to; + { SR.combine(EF, EF) } -> std::convertible_to; + { SR.identity() } -> std::convertible_to; +}; +template +concept IsSemiRingOf = IsSemiRing && requires { + requires std::same_as; +}; + template class SemiRing { public: using l_t = typename AnalysisDomainTy::l_t; + using EdgeFunctionType = EdgeFunction; virtual ~SemiRing() = default; @@ -28,7 +48,31 @@ template class SemiRing { const EdgeFunction &R) { return L.joinWith(R); } + + virtual EdgeFunctionType identity() const { return EdgeIdentity{}; } }; + +struct BinarySemiRing { + using EdgeFunctionType = EdgeIdentity; + + [[nodiscard]] constexpr EdgeFunctionType + extend(EdgeFunctionType /*L*/, EdgeFunctionType /*R*/) const noexcept { + return {}; + } + + [[nodiscard]] constexpr EdgeFunctionType + combine(EdgeFunctionType /*L*/, EdgeFunctionType /*R*/) const noexcept { + return {}; + } + + [[nodiscard]] constexpr EdgeFunctionType identity() const noexcept { + return {}; + } + + static constinit BinarySemiRing Instance; +}; + +inline constinit BinarySemiRing BinarySemiRing::Instance{}; } // namespace psr #endif // PHASAR_UTILS_SEMIRING_H diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt index b5de60ba49..f9b8ad4e24 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/CMakeLists.txt @@ -8,6 +8,7 @@ set(IfdsIdeSources SparseIDESolverTest.cpp IterativeIDESolverTest.cpp CFLFieldSensTest.cpp + WPDSSolverTest.cpp ) foreach(TEST_SRC ${IfdsIdeSources}) diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp new file mode 100644 index 0000000000..c005b0ffeb --- /dev/null +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp @@ -0,0 +1,186 @@ + +#include "phasar/DataFlow/WPDS/Solver/WPDSSolver.h" + +#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h" +#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h" +#include "phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h" +#include "phasar/DataFlow/IfdsIde/SolverResults.h" +#include "phasar/DataFlow/WPDS/IDERuleProvider.h" +#include "phasar/Domain/LatticeDomain.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDELinearConstantAnalysis.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/Printer.h" + +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/Support/TypeName.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +#include +#include + +using namespace psr; + +/* ============== TEST FIXTURE ============== */ +class WPDSSolverTest : public ::testing::TestWithParam { +protected: + static constexpr auto PathToLlFiles = + PHASAR_BUILD_SUBFOLDER("linear_constant/"); + + void doAnalysis(const llvm::Twine &LlvmFilePath, bool PrintDump = false) { + LLVMProjectIRDB IRDB(PathToLlFiles + LlvmFilePath); + DIBasedTypeHierarchy TH(IRDB); + LLVMAliasSet PT(&IRDB); + LLVMBasedICFG ICFG(&IRDB, CallGraphAnalysisType::OTF, {"main"}, &TH, &PT, + Soundness::Soundy, /*IncludeGlobals*/ true); + + IDELinearConstantAnalysis Problem(&IRDB, &ICFG, {"main"}); + IterativeIDESolver BaselineSolver(&Problem, &ICFG); + + auto Start = std::chrono::steady_clock::now(); + BaselineSolver.solve(); + auto End = std::chrono::steady_clock::now(); + auto NewTime = End - Start; + llvm::errs() << "IterativeIDESolver Elapsed:\t" << NewTime.count() + << "ns\n"; + + wpds::IDERuleProvider LCARules(&Problem, &ICFG); + WPDSSolver NewSolver(&LCARules, &Problem); + Start = std::chrono::steady_clock::now(); + NewSolver.solve(); + End = std::chrono::steady_clock::now(); + + auto OldTime = End - Start; + llvm::errs() << "WPDSSolver Elapsed:\t\t" << OldTime.count() << "ns\n"; + + if (PrintDump) { + BaselineSolver.dumpResults(); + NewSolver.getSolverResults().dumpResults(); + } + + checkEquality(BaselineSolver.getSolverResults(), + NewSolver.getSolverResults()); + } + + template + void checkEquality(const SR1 &LHS, const SR2 &RHS) { + for (const auto &[Row, ColVal] : LHS.getAllResultEntries()) { + for (const auto &[Col, Val] : ColVal) { + bool Holds = RHS.isInResultSet(Col, Row); + EXPECT_TRUE(Holds) << "The RHS does not contain fact " + << llvmIRToString(Col) << " at inst " + << llvmIRToString(Row); + + // TODO: Compare values + + // auto It = RHSColVal.find(Col); + // if (It != RHSColVal.end()) { + // EXPECT_TRUE(Val == It->second) + // << "The edge values at inst " << llvmIRToString(Row) + // << " and fact " << llvmIRToString(Col) << " do not match: " << + // Val + // << " vs " << It->second; + // } + } + } + for (const auto &[ColVal, Row] : RHS.getAllIFDSResultEntries()) { + if (llvm::isa(Row)) { + continue; + } + EXPECT_TRUE(LHS.containsNode(Row)) + << "The old results do not contain Node " << NToString(Row); + } + } + + void TearDown() override {} + +}; // Test Fixture + +// Using IDESolverConfig +TEST_P(WPDSSolverTest, IDESolverTestLCA) { doAnalysis(GetParam()); } + +static constexpr std::string_view LCATestFiles[] = { + "basic_01_cpp_dbg.ll", + "basic_02_cpp_dbg.ll", + "basic_03_cpp_dbg.ll", + "basic_04_cpp_dbg.ll", + "basic_05_cpp_dbg.ll", + "basic_06_cpp_dbg.ll", + "basic_07_cpp_dbg.ll", + "basic_08_cpp_dbg.ll", + "basic_09_cpp_dbg.ll", + "basic_10_cpp_dbg.ll", + "basic_11_cpp_dbg.ll", + "basic_12_cpp_dbg.ll", + + "branch_01_cpp_dbg.ll", + "branch_02_cpp_dbg.ll", + "branch_03_cpp_dbg.ll", + "branch_04_cpp_dbg.ll", + "branch_05_cpp_dbg.ll", + "branch_06_cpp_dbg.ll", + "branch_07_cpp_dbg.ll", + + "while_01_cpp_dbg.ll", + "while_02_cpp_dbg.ll", + "while_03_cpp_dbg.ll", + "while_04_cpp_dbg.ll", + "while_05_cpp_dbg.ll", + "for_01_cpp_dbg.ll", + + "call_01_cpp_dbg.ll", + "call_02_cpp_dbg.ll", + "call_03_cpp_dbg.ll", + "call_04_cpp_dbg.ll", + "call_05_cpp_dbg.ll", + "call_06_cpp_dbg.ll", + "call_07_cpp_dbg.ll", + "call_08_cpp_dbg.ll", + "call_09_cpp_dbg.ll", + "call_10_cpp_dbg.ll", + "call_11_cpp_dbg.ll", + + "recursion_01_cpp_dbg.ll", + "recursion_02_cpp_dbg.ll", + "recursion_03_cpp_dbg.ll", + + "global_01_cpp_dbg.ll", + "global_02_cpp_dbg.ll", + "global_03_cpp_dbg.ll", + "global_04_cpp_dbg.ll", + "global_05_cpp_dbg.ll", + "global_06_cpp_dbg.ll", + "global_07_cpp_dbg.ll", + "global_08_cpp_dbg.ll", + "global_09_cpp_dbg.ll", + "global_10_cpp_dbg.ll", + "global_11_cpp_dbg.ll", + "global_12_cpp_dbg.ll", + "global_13_cpp_dbg.ll", + "global_14_cpp_dbg.ll", + "global_15_cpp_dbg.ll", + "global_16_cpp_dbg.ll", + + "overflow_add_cpp_dbg.ll", + "overflow_sub_cpp_dbg.ll", + "overflow_mul_cpp_dbg.ll", + "overflow_div_min_by_neg_one_cpp_dbg.ll", + + "ub_division_by_zero_cpp_dbg.ll", + "ub_modulo_by_zero_cpp_dbg.ll", + "external_fun_cpp.ll", +}; + +INSTANTIATE_TEST_SUITE_P(WPDSSolverTest, WPDSSolverTest, + ::testing::ValuesIn(LCATestFiles)); + +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + return RUN_ALL_TESTS(); +} From e80ce630d5080ce9c8338ec00ebdeaf9ac3629a8 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 16:26:11 +0200 Subject: [PATCH 2/6] Add on-demand weight computation --- .../phasar/DataFlow/WPDS/IDERuleProvider.h | 19 ++- include/phasar/DataFlow/WPDS/RuleProvider.h | 5 +- .../phasar/DataFlow/WPDS/Solver/WPDSSolver.h | 8 +- .../DataFlow/WPDS/Solver/WPDSSolverResults.h | 117 ++++++++++++++++++ include/phasar/Utils/JoinLattice.h | 14 +++ include/phasar/Utils/Utilities.h | 21 ++++ .../DataFlow/IfdsIde/WPDSSolverTest.cpp | 27 ++-- 7 files changed, 186 insertions(+), 25 deletions(-) diff --git a/include/phasar/DataFlow/WPDS/IDERuleProvider.h b/include/phasar/DataFlow/WPDS/IDERuleProvider.h index 0315af1b3c..932726557b 100644 --- a/include/phasar/DataFlow/WPDS/IDERuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IDERuleProvider.h @@ -9,7 +9,7 @@ * Fabian Schiebel and others *****************************************************************************/ -#include "phasar/ControlFlow/SparseCFGProvider.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/WPDS/RuleProvider.h" #include "phasar/Utils/ByRef.h" @@ -21,6 +21,8 @@ namespace psr::wpds { template class IDERuleProvider { + using l_t = typename ProblemT::l_t; + public: using control_location_type = typename ProblemT::d_t; using stack_element_type = typename ProblemT::n_t; @@ -155,12 +157,21 @@ template class IDERuleProvider { } [[nodiscard]] auto initialSeeds() { - llvm::SmallVector> + llvm::SmallVector< + std::tuple> Outs; for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { - for (const auto &[Fact, _] : Facts) { - Outs.emplace_back(Fact, Inst); + for (const auto &[Fact, Val] : Facts) { + if (Val.isTop()) { + continue; + } + if (Val.isBottom()) { + Outs.emplace_back(Fact, Inst, AllBottom{}); + } else { + Outs.emplace_back(Fact, Inst, + ConstantEdgeFunction{Val.assertGetValue()}); + } } } diff --git a/include/phasar/DataFlow/WPDS/RuleProvider.h b/include/phasar/DataFlow/WPDS/RuleProvider.h index c51c151f0b..575d5d470b 100644 --- a/include/phasar/DataFlow/WPDS/RuleProvider.h +++ b/include/phasar/DataFlow/WPDS/RuleProvider.h @@ -42,8 +42,9 @@ concept RuleProvider = requires(T &RP, typename T::control_location_type CL, { RP.initialSeeds() - } -> psr::is_iterable_over_v>; + } -> psr::is_iterable_over_v< + std::tuple>; }; template diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h index 9e901ec4d4..73572cd6b0 100644 --- a/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h @@ -41,6 +41,7 @@ class WPDSSolver : private wpds::detail::SolverResultsData< using base_t::ESGNodeCompressor; using base_t::Incoming; using base_t::PathEdges; + using base_t::Seeds; public: using cl_t = typename RuleProviderT::control_location_type; @@ -235,9 +236,14 @@ class WPDSSolver : private wpds::detail::SolverResultsData< } void submitInitialSeeds() { - for (const auto &[InitCL, InitSE] : RP->initialSeeds()) { + for (const auto &[InitCL, InitSE, InitWeight] : RP->initialSeeds()) { auto Init = compressNode(InitCL, InitSE); propagate({Init, InitSE, InitCL}, SR->identity()); + + auto [It, Inserted] = Seeds.try_emplace(Init, InitWeight); + if (!Inserted) { + It->second = SR->combine(It->second, InitWeight); + } } } diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h index 59314fea9d..77069594e4 100644 --- a/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h @@ -4,16 +4,33 @@ #include "phasar/Utils/MapUtils.h" #include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/Printer.h" +#include "phasar/Utils/SemiRing.h" #include "phasar/Utils/StrongTypeDef.h" +#include "phasar/Utils/TypeTraits.h" #include "phasar/Utils/TypedVector.h" +#include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" +#include +#include + PHASAR_STRONG_TYPEDEF(psr::wpds, uint32_t, ESGNodeId); namespace psr::wpds { + +template struct ComputeWeightsCache { + using weight_t = WeightTy; + + /// Don't call this ctor directly. Use createWeightCache() from the + /// SolverResults instead + explicit ComputeWeightsCache(std::true_type /*For internal use only*/) {} + + llvm::DenseMap> Data{}; +}; + namespace detail { template @@ -29,6 +46,7 @@ struct SolverResultsData { TypedVector, weight_t>> Incoming; + llvm::SmallDenseMap Seeds{}; }; template @@ -109,6 +127,55 @@ class SolverResultsBase { return true; } + /// Computes the weight for the given pair, accumulated over all + /// accepting paths starting there. Re-use the WeightCache across calls to + /// this function for better performance. + /// + /// Should only be called if isInResultSet(Fact, At) is true. + [[nodiscard]] weight_t + computeWeightAt(ByConstRef Fact, ByConstRef At, + IsSemiRing auto &SR, + ComputeWeightsCache &WeightCache) const { + + const auto &PEs = getOrDefault(self().pathEdges(), std::pair{Fact, At}); + return mapCombine( + PEs, SR, + [this, &SR, &WeightCache](const auto &SrcAndWeight) { + const auto &[Src, Weight] = SrcAndWeight; + auto IncVal = this->computeWeightAtRec(Src, SR, WeightCache); + return SR.extend(std::move(IncVal), Weight); + }, + SecondFn{}); + } + + /// Computes the weight for the given pair, accumulated over all + /// accepting paths starting there. + /// + /// Use the overload of this function taking a ComputeWeightsCache (can be + /// initialized via createWeightCache()) for better batch performance if + /// calling this function multiple times. + /// + /// Should only be called if isInResultSet(Fact, At) is true. + [[nodiscard]] weight_t computeWeightAt(ByConstRef Fact, + ByConstRef At, + IsSemiRing auto &SR) const { + auto WeightCache = createWeightCache(); + return computeWeightAt(Fact, At, SR, WeightCache); + } + + /// Creates and initializes a new ComputeWeightsCache that can be used to + /// speed-up consecutive calls to computeWeightAt() + [[nodiscard]] auto createWeightCache() const { + ComputeWeightsCache WeightCache{std::true_type{}}; + WeightCache.Data.reserve(self().seeds().size()); + + for (const auto &[SeedNod, SeedWeight] : self().seeds()) { + WeightCache.Data.try_emplace(SeedNod, SeedWeight); + } + + return WeightCache; + } + [[nodiscard]] auto getAllIFDSResultEntries() const { return llvm::make_filter_range(self().esgNodeCompressor(), [this](const auto &Entry) { @@ -123,6 +190,54 @@ class SolverResultsBase { void dumpResults(llvm::raw_ostream &OS = llvm::outs()); private: + [[nodiscard]] weight_t + computeWeightAtRec(ESGNodeId Src, IsSemiRing auto &SR, + ComputeWeightsCache &WeightCache) const { + auto [It, Inserted] = WeightCache.Data.try_emplace(Src); + if (It->second) { + return *It->second; + } + if (!Inserted) { + // cycle detection + + // TODO: Is that correct? + return SR.identity(); + } + + const auto &Inc = self().incoming()[Src]; + + auto Ret = mapCombine( + Inc, SR, + [this, &SR, &WeightCache](const auto &SrcSEAndWeight) { + const auto &[SrcSE, Weight] = SrcSEAndWeight; + const auto &[SrcSrc, _] = SrcSE; + + // XXX: Can we get rid of this recursion? + auto IncVal = this->computeWeightAtRec(SrcSrc, SR, WeightCache); + return SR.extend(std::move(IncVal), Weight); + }, + SecondFn{}); + It->second.emplace(std::move(Ret)); + return *It->second; + } + + [[nodiscard]] weight_t mapCombine(auto &&Range, IsSemiRing auto &SR, + auto Transform, + auto Projection = IdentityFn{}) const { + auto It = llvm::adl_begin(Range); + auto End = llvm::adl_end(Range); + + if (It == End) { + return SR.identity(); + } + + auto Ret = std::invoke(Transform, *It); + for (++It; It != End; ++It) { + Ret = SR.combine(std::move(Ret), std::invoke(Projection, *It)); + } + return Ret; + } + constexpr SolverResultsBase() noexcept = default; friend Derived; @@ -253,6 +368,7 @@ class OwningSolverResults [[nodiscard]] const auto &esgNodeCompressor() const noexcept { return Data.ESGNodeCompressor; } + [[nodiscard]] const auto &seeds() const noexcept { return Data.Seeds; } detail::SolverResultsData Data; }; @@ -278,6 +394,7 @@ class SolverResults [[nodiscard]] const auto &esgNodeCompressor() const noexcept { return Data->ESGNodeCompressor; } + [[nodiscard]] const auto &seeds() const noexcept { return Data->Seeds; } NonNullPtr> Data; }; diff --git a/include/phasar/Utils/JoinLattice.h b/include/phasar/Utils/JoinLattice.h index 7b4b6a1870..e2f120fee2 100644 --- a/include/phasar/Utils/JoinLattice.h +++ b/include/phasar/Utils/JoinLattice.h @@ -38,6 +38,20 @@ concept HasJoinLatticeTraits = requires(const L &Val) { { JoinLatticeTraits::join(Val, Val) } -> std::convertible_to; }; +template +concept IsJoinLattice = requires(T &Lat, const typename T::l_t &Val) { + typename T::l_t; + + { Lat.topElement() } -> std::convertible_to; + { Lat.bottomElement() } -> std::convertible_to; + { Lat.join(Val, Val) } -> std::convertible_to; +}; + +template +concept IsJoinLatticeFor = IsJoinLattice && requires { + requires std::convertible_to; +}; + template class JoinLattice { public: using l_t = typename AnalysisDomainTy::l_t; diff --git a/include/phasar/Utils/Utilities.h b/include/phasar/Utils/Utilities.h index d9cddb7531..0b534246eb 100644 --- a/include/phasar/Utils/Utilities.h +++ b/include/phasar/Utils/Utilities.h @@ -284,6 +284,27 @@ struct identity { } }; +struct FirstFn { + [[nodiscard]] constexpr decltype(auto) + operator()(const auto &PairLike) const noexcept { + if constexpr (requires { PairLike.first; }) { + return PairLike.first; + } else { + return std::get<0>(PairLike); + } + } +}; +struct SecondFn { + [[nodiscard]] constexpr decltype(auto) + operator()(const auto &PairLike) const noexcept { + if constexpr (requires { PairLike.second; }) { + return PairLike.second; + } else { + return std::get<1>(PairLike); + } + } +}; + template llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const std::optional &Opt) { diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp index c005b0ffeb..a2746cec5b 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp @@ -1,11 +1,7 @@ #include "phasar/DataFlow/WPDS/Solver/WPDSSolver.h" -#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h" -#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" #include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h" -#include "phasar/DataFlow/IfdsIde/Solver/StaticIDESolverConfig.h" -#include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/DataFlow/WPDS/IDERuleProvider.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" @@ -17,13 +13,11 @@ #include "phasar/Utils/Printer.h" #include "llvm/IR/IntrinsicInst.h" -#include "llvm/Support/TypeName.h" #include "TestConfig.h" #include "gtest/gtest.h" #include -#include using namespace psr; @@ -65,11 +59,11 @@ class WPDSSolverTest : public ::testing::TestWithParam { } checkEquality(BaselineSolver.getSolverResults(), - NewSolver.getSolverResults()); + NewSolver.getSolverResults(), Problem); } - template - void checkEquality(const SR1 &LHS, const SR2 &RHS) { + template + void checkEquality(const SR1 &LHS, const SR2 &RHS, ProblemT &Problem) { for (const auto &[Row, ColVal] : LHS.getAllResultEntries()) { for (const auto &[Col, Val] : ColVal) { bool Holds = RHS.isInResultSet(Col, Row); @@ -77,16 +71,13 @@ class WPDSSolverTest : public ::testing::TestWithParam { << llvmIRToString(Col) << " at inst " << llvmIRToString(Row); - // TODO: Compare values + auto RHSWeight = RHS.computeWeightAt(Col, Row, Problem); + auto RHSVal = RHSWeight.computeTarget(Bottom{}); - // auto It = RHSColVal.find(Col); - // if (It != RHSColVal.end()) { - // EXPECT_TRUE(Val == It->second) - // << "The edge values at inst " << llvmIRToString(Row) - // << " and fact " << llvmIRToString(Col) << " do not match: " << - // Val - // << " vs " << It->second; - // } + EXPECT_TRUE(Val == RHSVal) + << "The edge values at inst " << llvmIRToString(Row) << " and fact " + << llvmIRToString(Col) << " do not match: " << Val << " vs " + << RHSVal; } } for (const auto &[ColVal, Row] : RHS.getAllIFDSResultEntries()) { From 2d9771a015499b546434aec3d7ff30d5d3fc3ee6 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 17:00:47 +0200 Subject: [PATCH 3/6] Some cleanup --- include/phasar/DataFlow/WPDS/RuleProvider.h | 2 ++ .../phasar/DataFlow/WPDS/Solver/WPDSSolver.h | 11 +++-------- .../DataFlow/WPDS/Solver/WPDSSolverResults.h | 17 ++++++----------- include/phasar/Utils/SemiRing.h | 5 +---- .../DataFlow/IfdsIde/WPDSSolverTest.cpp | 8 ++++---- 5 files changed, 16 insertions(+), 27 deletions(-) diff --git a/include/phasar/DataFlow/WPDS/RuleProvider.h b/include/phasar/DataFlow/WPDS/RuleProvider.h index 575d5d470b..7ef69cff15 100644 --- a/include/phasar/DataFlow/WPDS/RuleProvider.h +++ b/include/phasar/DataFlow/WPDS/RuleProvider.h @@ -11,6 +11,8 @@ #include "phasar/Utils/TypeTraits.h" +#include "llvm/ADT/ArrayRef.h" + #include namespace psr::wpds { diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h index 73572cd6b0..c068bb94da 100644 --- a/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolver.h @@ -75,8 +75,8 @@ class WPDSSolver : private wpds::detail::SolverResultsData< } [[nodiscard]] wpds::OwningSolverResults - consumeSolverResults() const noexcept { - return {std::move(this)}; + consumeSolverResults() noexcept { + return {std::move(static_cast(*this))}; } private: @@ -199,11 +199,6 @@ class WPDSSolver : private wpds::detail::SolverResultsData< }); } - // else { - - // llvm::errs() << "NO injectAdditionalPushEdges:\n"; - // } - if (RP->hasPopRules(CurrTgtFact, CurrTarget)) { if (!addEndSummary(CurrSrc, CurrTgtFact, CurrTarget, CurrWeight)) { @@ -306,7 +301,7 @@ class WPDSSolver : private wpds::detail::SolverResultsData< std::pair{std::move(TgtFact), std::move(TgtSE)}, std::move(TgtWeight)); if (!Inserted) { auto Merged = SR->combine(It->second, TgtWeight); - if (It->second != TgtWeight) { + if (It->second != Merged) { It->second = std::move(Merged); Inserted = true; } diff --git a/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h index 77069594e4..8240d44402 100644 --- a/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h +++ b/include/phasar/DataFlow/WPDS/Solver/WPDSSolverResults.h @@ -139,13 +139,11 @@ class SolverResultsBase { const auto &PEs = getOrDefault(self().pathEdges(), std::pair{Fact, At}); return mapCombine( - PEs, SR, - [this, &SR, &WeightCache](const auto &SrcAndWeight) { + PEs, SR, [this, &SR, &WeightCache](const auto &SrcAndWeight) { const auto &[Src, Weight] = SrcAndWeight; auto IncVal = this->computeWeightAtRec(Src, SR, WeightCache); return SR.extend(std::move(IncVal), Weight); - }, - SecondFn{}); + }); } /// Computes the weight for the given pair, accumulated over all @@ -207,23 +205,20 @@ class SolverResultsBase { const auto &Inc = self().incoming()[Src]; auto Ret = mapCombine( - Inc, SR, - [this, &SR, &WeightCache](const auto &SrcSEAndWeight) { + Inc, SR, [this, &SR, &WeightCache](const auto &SrcSEAndWeight) { const auto &[SrcSE, Weight] = SrcSEAndWeight; const auto &[SrcSrc, _] = SrcSE; // XXX: Can we get rid of this recursion? auto IncVal = this->computeWeightAtRec(SrcSrc, SR, WeightCache); return SR.extend(std::move(IncVal), Weight); - }, - SecondFn{}); + }); It->second.emplace(std::move(Ret)); return *It->second; } [[nodiscard]] weight_t mapCombine(auto &&Range, IsSemiRing auto &SR, - auto Transform, - auto Projection = IdentityFn{}) const { + auto Transform) const { auto It = llvm::adl_begin(Range); auto End = llvm::adl_end(Range); @@ -233,7 +228,7 @@ class SolverResultsBase { auto Ret = std::invoke(Transform, *It); for (++It; It != End; ++It) { - Ret = SR.combine(std::move(Ret), std::invoke(Projection, *It)); + Ret = SR.combine(std::move(Ret), std::invoke(Transform, *It)); } return Ret; } diff --git a/include/phasar/Utils/SemiRing.h b/include/phasar/Utils/SemiRing.h index 5dba6c6d54..0976e1d809 100644 --- a/include/phasar/Utils/SemiRing.h +++ b/include/phasar/Utils/SemiRing.h @@ -7,8 +7,7 @@ * Fabian Schiebel and others *****************************************************************************/ -#ifndef PHASAR_UTILS_SEMIRING_H -#define PHASAR_UTILS_SEMIRING_H +#pragma once #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" @@ -74,5 +73,3 @@ struct BinarySemiRing { inline constinit BinarySemiRing BinarySemiRing::Instance{}; } // namespace psr - -#endif // PHASAR_UTILS_SEMIRING_H diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp index a2746cec5b..b202a79bbe 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp @@ -40,8 +40,8 @@ class WPDSSolverTest : public ::testing::TestWithParam { auto Start = std::chrono::steady_clock::now(); BaselineSolver.solve(); auto End = std::chrono::steady_clock::now(); - auto NewTime = End - Start; - llvm::errs() << "IterativeIDESolver Elapsed:\t" << NewTime.count() + auto BaselineTime = End - Start; + llvm::errs() << "IterativeIDESolver Elapsed:\t" << BaselineTime.count() << "ns\n"; wpds::IDERuleProvider LCARules(&Problem, &ICFG); @@ -50,8 +50,8 @@ class WPDSSolverTest : public ::testing::TestWithParam { NewSolver.solve(); End = std::chrono::steady_clock::now(); - auto OldTime = End - Start; - llvm::errs() << "WPDSSolver Elapsed:\t\t" << OldTime.count() << "ns\n"; + auto WPDSTime = End - Start; + llvm::errs() << "WPDSSolver Elapsed:\t\t" << WPDSTime.count() << "ns\n"; if (PrintDump) { BaselineSolver.dumpResults(); From 0fe3c31443deb124b8ffe00e5831dbe274a609ce Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 17:30:37 +0200 Subject: [PATCH 4/6] minor --- include/phasar/DataFlow/WPDS/IDERuleProvider.h | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/include/phasar/DataFlow/WPDS/IDERuleProvider.h b/include/phasar/DataFlow/WPDS/IDERuleProvider.h index 932726557b..fbb8727528 100644 --- a/include/phasar/DataFlow/WPDS/IDERuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IDERuleProvider.h @@ -48,7 +48,8 @@ template class IDERuleProvider { if (ICF->isCallSite(SE)) { auto Callees = ICF->getCalleesOfCallAt(SE); - for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { + auto RetSites = ICF->getReturnSitesOfCallAt(SE); + for (const auto &Succ : RetSites) { auto Facts = FECache.getCallToRetFlowFunction(SE, Succ, Callees) ->computeTargets(CL); for (auto &Fct : Facts) { @@ -60,7 +61,7 @@ template class IDERuleProvider { for (const auto &DestFun : Callees) { if (auto SumFF = FECache.getSummaryFlowFunction(SE, DestFun)) { auto Facts = SumFF->computeTargets(CL); - for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { + for (const auto &Succ : RetSites) { for (auto &Fct : Facts) { auto W = FECache.getSummaryEdgeFunction(SE, CL, Succ, Fct); Outs.emplace_back(Fct, Succ, std::move(W)); @@ -97,6 +98,7 @@ template class IDERuleProvider { << "; SE=" << NToString(SE)); auto Callees = ICF->getCalleesOfCallAt(SE); + auto RetSites = ICF->getReturnSitesOfCallAt(SE); for (const auto &DestFun : Callees) { if (FECache.getSummaryFlowFunction(SE, DestFun)) { // Handled in getNormalRules() @@ -104,11 +106,12 @@ template class IDERuleProvider { } auto Facts = FECache.getCallFlowFunction(SE, DestFun)->computeTargets(CL); - for (const auto &EntrySE : ICF->getStartPointsOf(DestFun)) { - for (const auto &Succ : ICF->getReturnSitesOfCallAt(SE)) { - for (auto &&Fct : Facts) { - auto W = FECache.getCallEdgeFunction(SE, CL, DestFun, Fct); - Outs.emplace_back(Fct, Succ, EntrySE, std::move(W)); + auto EntrySEs = ICF->getStartPointsOf(DestFun); + for (auto &&Fct : Facts) { + auto W = FECache.getCallEdgeFunction(SE, CL, DestFun, Fct); + for (const auto &EntrySE : EntrySEs) { + for (const auto &Succ : RetSites) { + Outs.emplace_back(Fct, Succ, EntrySE, W); } } } From c12838c0c7b78b6b7b72438e6518824a14ce4be4 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 18:50:10 +0200 Subject: [PATCH 5/6] Add IFDSRuleProvider --- .../DataFlow/IfdsIde/EdgeFunctionUtils.h | 3 + .../phasar/DataFlow/WPDS/IDERuleProvider.h | 16 +- .../phasar/DataFlow/WPDS/IFDSRuleProvider.h | 182 ++++++++++++++++++ .../DataFlow/IfdsIde/WPDSSolverTest.cpp | 68 +++++-- 4 files changed, 243 insertions(+), 26 deletions(-) create mode 100644 include/phasar/DataFlow/WPDS/IFDSRuleProvider.h diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h index 3aef8bba78..1c62849f21 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h @@ -44,6 +44,9 @@ template struct EdgeIdentity final { friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, EdgeIdentity) { return OS << "EdgeIdentity"; } + + friend constexpr bool operator==(EdgeIdentity, + EdgeIdentity) noexcept = default; }; template struct ConstantEdgeFunction { diff --git a/include/phasar/DataFlow/WPDS/IDERuleProvider.h b/include/phasar/DataFlow/WPDS/IDERuleProvider.h index fbb8727528..7c438d2a01 100644 --- a/include/phasar/DataFlow/WPDS/IDERuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IDERuleProvider.h @@ -13,6 +13,7 @@ #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/WPDS/RuleProvider.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Printer.h" #include "phasar/Utils/Utilities.h" @@ -166,14 +167,19 @@ template class IDERuleProvider { for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { for (const auto &[Fact, Val] : Facts) { - if (Val.isTop()) { + if (Val == Problem->topElement()) { continue; } - if (Val.isBottom()) { - Outs.emplace_back(Fact, Inst, AllBottom{}); + if (Val == Problem->bottomElement()) { + if constexpr (HasJoinLatticeTraits) { + Outs.emplace_back(Fact, Inst, AllBottom{}); + } else { + Outs.emplace_back(Fact, Inst, AllBottom{Val}); + } } else { - Outs.emplace_back(Fact, Inst, - ConstantEdgeFunction{Val.assertGetValue()}); + Outs.emplace_back( + Fact, Inst, + ConstantEdgeFunction{NonTopBotValue::unwrap(Val)}); } } } diff --git a/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h b/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h new file mode 100644 index 0000000000..a564ce267e --- /dev/null +++ b/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h @@ -0,0 +1,182 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" +#include "phasar/DataFlow/WPDS/RuleProvider.h" +#include "phasar/Domain/BinaryDomain.h" +#include "phasar/Utils/ByRef.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/Printer.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/SmallVector.h" + +namespace psr::wpds { +template class IFDSRuleProvider { + using l_t = BinaryDomain; + +public: + using control_location_type = typename ProblemT::d_t; + using stack_element_type = typename ProblemT::n_t; + using weight_type = EdgeIdentity; + + static constexpr llvm::StringLiteral LogCategory = "IDERuleProvider"; + + IFDSRuleProvider(ProblemT *Problem, const ICFGTy *ICF) noexcept + : Problem(&assertNotNull(Problem)), ICF(&assertNotNull(ICF)) { + static_assert(RuleProvider); + } + + [[nodiscard]] auto getNormalRules(ByConstRef CL, + ByConstRef SE) { + + llvm::SmallVector< + std::tuple> + Outs; + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getNormalRules]: CL=" << DToString(CL) + << "; SE=" << NToString(SE)); + + if (ICF->isCallSite(SE)) { + auto Callees = ICF->getCalleesOfCallAt(SE); + auto RetSites = ICF->getReturnSitesOfCallAt(SE); + for (const auto &Succ : RetSites) { + auto Facts = FECache.getCallToRetFlowFunction(SE, Succ, Callees) + ->computeTargets(CL); + for (auto &Fct : Facts) { + Outs.emplace_back(std::move(Fct), Succ, weight_type{}); + } + } + + for (const auto &DestFun : Callees) { + if (auto SumFF = FECache.getSummaryFlowFunction(SE, DestFun)) { + auto Facts = SumFF->computeTargets(CL); + for (const auto &Succ : RetSites) { + for (auto &Fct : Facts) { + Outs.emplace_back(Fct, Succ, weight_type{}); + } + } + } + } + + } else { + for (const auto &Succ : ICF->getSuccsOf(SE)) { + auto Facts = + FECache.getNormalFlowFunction(SE, Succ)->computeTargets(CL); + for (auto &Fct : Facts) { + Outs.emplace_back(std::move(Fct), Succ, weight_type{}); + } + } + } + + return Outs; + } + + [[nodiscard]] auto getPushRules(ByConstRef CL, + ByConstRef SE) { + llvm::SmallVector> + Outs; + if (!ICF->isCallSite(SE)) { + return Outs; + } + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getPushRules]: CL=" << DToString(CL) + << "; SE=" << NToString(SE)); + + auto Callees = ICF->getCalleesOfCallAt(SE); + auto RetSites = ICF->getReturnSitesOfCallAt(SE); + for (const auto &DestFun : Callees) { + if (FECache.getSummaryFlowFunction(SE, DestFun)) { + // Handled in getNormalRules() + continue; + } + + auto Facts = FECache.getCallFlowFunction(SE, DestFun)->computeTargets(CL); + auto EntrySEs = ICF->getStartPointsOf(DestFun); + for (const auto &EntrySE : EntrySEs) { + for (const auto &Succ : RetSites) { + for (auto &&Fct : Facts) { + Outs.emplace_back(Fct, Succ, EntrySE, weight_type{}); + } + } + } + } + + return Outs; + } + + [[nodiscard]] bool hasPopRules(ByConstRef /*CL*/, + ByConstRef SE) { + // TODO: Be more precise here, filtering for facts CL that we actually need + // in the summary. + return ICF->isExitInst(SE); + } + + [[nodiscard]] auto getPopRules(ByConstRef CL, + ByConstRef ExitSE, + ByConstRef RetSiteSE, + ByConstRef /*EntrySE*/ + ) { + llvm::SmallVector> Outs; + + PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, + "[getPopRules]: CL=" + << DToString(CL) + << "; ExitSE=" << NToString(ExitSE) + << "; RetSiteSE=" << NToString(RetSiteSE)); + + auto DestFun = ICF->getFunctionOf(ExitSE); + + for (const auto &CS : ICF->getPredsOf(RetSiteSE)) { + if (!ICF->isCallSite(CS)) { + continue; + } + + auto Facts = FECache.getRetFlowFunction(CS, DestFun, ExitSE, RetSiteSE) + ->computeTargets(CL); + for (auto &Fct : Facts) { + Outs.emplace_back(std::move(Fct), weight_type{}); + } + } + + return Outs; + } + + [[nodiscard]] auto initialSeeds() { + llvm::SmallVector< + std::tuple> + Outs; + + for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { + for (const auto &[Fact, _] : Facts) { + Outs.emplace_back(Fact, Inst, weight_type{}); + } + } + + return Outs; + } + + [[nodiscard]] constexpr auto &ifdsProblem() const noexcept { + return *Problem; + } + +private: + ProblemT *Problem{}; + const ICFGTy *ICF{}; + + FlowEdgeFunctionCache FECache{ + *Problem}; +}; +} // namespace psr::wpds diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp index b202a79bbe..5e25dd4de0 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp @@ -3,6 +3,7 @@ #include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h" #include "phasar/DataFlow/WPDS/IDERuleProvider.h" +#include "phasar/DataFlow/WPDS/IFDSRuleProvider.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -11,6 +12,7 @@ #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Printer.h" +#include "phasar/Utils/SemiRing.h" #include "llvm/IR/IntrinsicInst.h" @@ -18,6 +20,7 @@ #include "gtest/gtest.h" #include +#include using namespace psr; @@ -27,6 +30,27 @@ class WPDSSolverTest : public ::testing::TestWithParam { static constexpr auto PathToLlFiles = PHASAR_BUILD_SUBFOLDER("linear_constant/"); + auto doWPDSAnalysis(auto &Problem, auto &ICFG) { + wpds::IDERuleProvider LCARules(&Problem, &ICFG); + WPDSSolver NewSolver(&LCARules, &Problem); + auto Start = std::chrono::steady_clock::now(); + NewSolver.solve(); + auto End = std::chrono::steady_clock::now(); + llvm::errs() << "WPDSSolver Elapsed:\t\t" << (End - Start).count() + << "ns\n"; + return NewSolver.consumeSolverResults(); + } + auto doPDSAnalysis(auto &Problem, auto &ICFG) { + wpds::IFDSRuleProvider LCARules(&Problem, &ICFG); + WPDSSolver NewSolver(&LCARules, &BinarySemiRing::Instance); + auto Start = std::chrono::steady_clock::now(); + NewSolver.solve(); + auto End = std::chrono::steady_clock::now(); + llvm::errs() << "PDSSolver Elapsed:\t\t" << (End - Start).count() << "ns\n"; + return NewSolver.consumeSolverResults(); + } + + template void doAnalysis(const llvm::Twine &LlvmFilePath, bool PrintDump = false) { LLVMProjectIRDB IRDB(PathToLlFiles + LlvmFilePath); DIBasedTypeHierarchy TH(IRDB); @@ -44,40 +68,41 @@ class WPDSSolverTest : public ::testing::TestWithParam { llvm::errs() << "IterativeIDESolver Elapsed:\t" << BaselineTime.count() << "ns\n"; - wpds::IDERuleProvider LCARules(&Problem, &ICFG); - WPDSSolver NewSolver(&LCARules, &Problem); - Start = std::chrono::steady_clock::now(); - NewSolver.solve(); - End = std::chrono::steady_clock::now(); - - auto WPDSTime = End - Start; - llvm::errs() << "WPDSSolver Elapsed:\t\t" << WPDSTime.count() << "ns\n"; + auto NewResults = [&] { + if constexpr (DoIDEAnalysis) { + return doWPDSAnalysis(Problem, ICFG); + } else { + return doPDSAnalysis(Problem, ICFG); + } + }(); if (PrintDump) { BaselineSolver.dumpResults(); - NewSolver.getSolverResults().dumpResults(); + NewResults.dumpResults(); } - checkEquality(BaselineSolver.getSolverResults(), - NewSolver.getSolverResults(), Problem); + checkEquality(BaselineSolver.getSolverResults(), NewResults, Problem, + std::bool_constant{}); } - template - void checkEquality(const SR1 &LHS, const SR2 &RHS, ProblemT &Problem) { + template + void checkEquality(const SR1 &LHS, const SR2 &RHS, ProblemT &Problem, + std::bool_constant) { for (const auto &[Row, ColVal] : LHS.getAllResultEntries()) { for (const auto &[Col, Val] : ColVal) { bool Holds = RHS.isInResultSet(Col, Row); EXPECT_TRUE(Holds) << "The RHS does not contain fact " << llvmIRToString(Col) << " at inst " << llvmIRToString(Row); - - auto RHSWeight = RHS.computeWeightAt(Col, Row, Problem); - auto RHSVal = RHSWeight.computeTarget(Bottom{}); - - EXPECT_TRUE(Val == RHSVal) - << "The edge values at inst " << llvmIRToString(Row) << " and fact " - << llvmIRToString(Col) << " do not match: " << Val << " vs " - << RHSVal; + if constexpr (CompareValues) { + auto RHSWeight = RHS.computeWeightAt(Col, Row, Problem); + auto RHSVal = RHSWeight.computeTarget(Bottom{}); + + EXPECT_TRUE(Val == RHSVal) + << "The edge values at inst " << llvmIRToString(Row) + << " and fact " << llvmIRToString(Col) << " do not match: " << Val + << " vs " << RHSVal; + } } } for (const auto &[ColVal, Row] : RHS.getAllIFDSResultEntries()) { @@ -95,6 +120,7 @@ class WPDSSolverTest : public ::testing::TestWithParam { // Using IDESolverConfig TEST_P(WPDSSolverTest, IDESolverTestLCA) { doAnalysis(GetParam()); } +TEST_P(WPDSSolverTest, IFDSSolverTestLCA) { doAnalysis(GetParam()); } static constexpr std::string_view LCATestFiles[] = { "basic_01_cpp_dbg.ll", From 86b0044b75d13fadb5307abfb14bcbc44b328453 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 21 May 2026 19:14:35 +0200 Subject: [PATCH 6/6] Consolidate IFDS and IDE RuleProvide into one to reduce code duplication (similar to how IterativeIDESolver approaches this) --- .../phasar/DataFlow/WPDS/IFDSRuleProvider.h | 182 ------------------ ...DERuleProvider.h => IfdsIdeRuleProvider.h} | 107 +++++++--- .../DataFlow/IfdsIde/WPDSSolverTest.cpp | 47 ++--- 3 files changed, 99 insertions(+), 237 deletions(-) delete mode 100644 include/phasar/DataFlow/WPDS/IFDSRuleProvider.h rename include/phasar/DataFlow/WPDS/{IDERuleProvider.h => IfdsIdeRuleProvider.h} (62%) diff --git a/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h b/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h deleted file mode 100644 index a564ce267e..0000000000 --- a/include/phasar/DataFlow/WPDS/IFDSRuleProvider.h +++ /dev/null @@ -1,182 +0,0 @@ -#pragma once - -/****************************************************************************** - * Copyright (c) 2026 Fabian Schiebel. - * All rights reserved. This program and the accompanying materials are made - * available under the terms of LICENSE.txt. - * - * Contributors: - * Fabian Schiebel and others - *****************************************************************************/ - -#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" -#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" -#include "phasar/DataFlow/WPDS/RuleProvider.h" -#include "phasar/Domain/BinaryDomain.h" -#include "phasar/Utils/ByRef.h" -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/Printer.h" -#include "phasar/Utils/Utilities.h" - -#include "llvm/ADT/SmallVector.h" - -namespace psr::wpds { -template class IFDSRuleProvider { - using l_t = BinaryDomain; - -public: - using control_location_type = typename ProblemT::d_t; - using stack_element_type = typename ProblemT::n_t; - using weight_type = EdgeIdentity; - - static constexpr llvm::StringLiteral LogCategory = "IDERuleProvider"; - - IFDSRuleProvider(ProblemT *Problem, const ICFGTy *ICF) noexcept - : Problem(&assertNotNull(Problem)), ICF(&assertNotNull(ICF)) { - static_assert(RuleProvider); - } - - [[nodiscard]] auto getNormalRules(ByConstRef CL, - ByConstRef SE) { - - llvm::SmallVector< - std::tuple> - Outs; - - PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, - "[getNormalRules]: CL=" << DToString(CL) - << "; SE=" << NToString(SE)); - - if (ICF->isCallSite(SE)) { - auto Callees = ICF->getCalleesOfCallAt(SE); - auto RetSites = ICF->getReturnSitesOfCallAt(SE); - for (const auto &Succ : RetSites) { - auto Facts = FECache.getCallToRetFlowFunction(SE, Succ, Callees) - ->computeTargets(CL); - for (auto &Fct : Facts) { - Outs.emplace_back(std::move(Fct), Succ, weight_type{}); - } - } - - for (const auto &DestFun : Callees) { - if (auto SumFF = FECache.getSummaryFlowFunction(SE, DestFun)) { - auto Facts = SumFF->computeTargets(CL); - for (const auto &Succ : RetSites) { - for (auto &Fct : Facts) { - Outs.emplace_back(Fct, Succ, weight_type{}); - } - } - } - } - - } else { - for (const auto &Succ : ICF->getSuccsOf(SE)) { - auto Facts = - FECache.getNormalFlowFunction(SE, Succ)->computeTargets(CL); - for (auto &Fct : Facts) { - Outs.emplace_back(std::move(Fct), Succ, weight_type{}); - } - } - } - - return Outs; - } - - [[nodiscard]] auto getPushRules(ByConstRef CL, - ByConstRef SE) { - llvm::SmallVector> - Outs; - if (!ICF->isCallSite(SE)) { - return Outs; - } - - PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, - "[getPushRules]: CL=" << DToString(CL) - << "; SE=" << NToString(SE)); - - auto Callees = ICF->getCalleesOfCallAt(SE); - auto RetSites = ICF->getReturnSitesOfCallAt(SE); - for (const auto &DestFun : Callees) { - if (FECache.getSummaryFlowFunction(SE, DestFun)) { - // Handled in getNormalRules() - continue; - } - - auto Facts = FECache.getCallFlowFunction(SE, DestFun)->computeTargets(CL); - auto EntrySEs = ICF->getStartPointsOf(DestFun); - for (const auto &EntrySE : EntrySEs) { - for (const auto &Succ : RetSites) { - for (auto &&Fct : Facts) { - Outs.emplace_back(Fct, Succ, EntrySE, weight_type{}); - } - } - } - } - - return Outs; - } - - [[nodiscard]] bool hasPopRules(ByConstRef /*CL*/, - ByConstRef SE) { - // TODO: Be more precise here, filtering for facts CL that we actually need - // in the summary. - return ICF->isExitInst(SE); - } - - [[nodiscard]] auto getPopRules(ByConstRef CL, - ByConstRef ExitSE, - ByConstRef RetSiteSE, - ByConstRef /*EntrySE*/ - ) { - llvm::SmallVector> Outs; - - PHASAR_LOG_LEVEL_CAT(DEBUG, LogCategory, - "[getPopRules]: CL=" - << DToString(CL) - << "; ExitSE=" << NToString(ExitSE) - << "; RetSiteSE=" << NToString(RetSiteSE)); - - auto DestFun = ICF->getFunctionOf(ExitSE); - - for (const auto &CS : ICF->getPredsOf(RetSiteSE)) { - if (!ICF->isCallSite(CS)) { - continue; - } - - auto Facts = FECache.getRetFlowFunction(CS, DestFun, ExitSE, RetSiteSE) - ->computeTargets(CL); - for (auto &Fct : Facts) { - Outs.emplace_back(std::move(Fct), weight_type{}); - } - } - - return Outs; - } - - [[nodiscard]] auto initialSeeds() { - llvm::SmallVector< - std::tuple> - Outs; - - for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { - for (const auto &[Fact, _] : Facts) { - Outs.emplace_back(Fact, Inst, weight_type{}); - } - } - - return Outs; - } - - [[nodiscard]] constexpr auto &ifdsProblem() const noexcept { - return *Problem; - } - -private: - ProblemT *Problem{}; - const ICFGTy *ICF{}; - - FlowEdgeFunctionCache FECache{ - *Problem}; -}; -} // namespace psr::wpds diff --git a/include/phasar/DataFlow/WPDS/IDERuleProvider.h b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h similarity index 62% rename from include/phasar/DataFlow/WPDS/IDERuleProvider.h rename to include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h index 7c438d2a01..283f851616 100644 --- a/include/phasar/DataFlow/WPDS/IDERuleProvider.h +++ b/include/phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h @@ -12,6 +12,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h" #include "phasar/DataFlow/WPDS/RuleProvider.h" +#include "phasar/Domain/BinaryDomain.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" @@ -20,25 +21,40 @@ #include "llvm/ADT/SmallVector.h" +#include + namespace psr::wpds { -template class IDERuleProvider { - using l_t = typename ProblemT::l_t; +namespace detail { +template struct WeightTypeOf { + using type = EdgeIdentity; +}; +template struct WeightTypeOf { + using type = typename ProblemT::EdgeFunctionType; +}; +} // namespace detail + +template +class IfdsIdeRuleProvider { public: using control_location_type = typename ProblemT::d_t; using stack_element_type = typename ProblemT::n_t; - using weight_type = typename ProblemT::EdgeFunctionType; + using weight_type = + typename detail::WeightTypeOf::type; - static constexpr llvm::StringLiteral LogCategory = "IDERuleProvider"; + static constexpr auto LogCategory = + ComputeWeights ? llvm::StringLiteral("IDERuleProvider") + : llvm::StringLiteral("IFDSRuleProvider"); - IDERuleProvider(ProblemT *Problem, const ICFGTy *ICF) noexcept + IfdsIdeRuleProvider( + ProblemT *Problem, const ICFGTy *ICF, + std::bool_constant /*unused*/ = {}) noexcept : Problem(&assertNotNull(Problem)), ICF(&assertNotNull(ICF)) { - static_assert(RuleProvider); + static_assert(RuleProvider); } [[nodiscard]] auto getNormalRules(ByConstRef CL, ByConstRef SE) { - llvm::SmallVector< std::tuple> Outs; @@ -54,8 +70,13 @@ template class IDERuleProvider { auto Facts = FECache.getCallToRetFlowFunction(SE, Succ, Callees) ->computeTargets(CL); for (auto &Fct : Facts) { - auto W = FECache.getCallToRetEdgeFunction(SE, CL, Succ, Fct, Callees); - Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + if constexpr (ComputeWeights) { + auto W = + FECache.getCallToRetEdgeFunction(SE, CL, Succ, Fct, Callees); + Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + } else { + Outs.emplace_back(std::move(Fct), Succ, weight_type{}); + } } } @@ -64,8 +85,12 @@ template class IDERuleProvider { auto Facts = SumFF->computeTargets(CL); for (const auto &Succ : RetSites) { for (auto &Fct : Facts) { - auto W = FECache.getSummaryEdgeFunction(SE, CL, Succ, Fct); - Outs.emplace_back(Fct, Succ, std::move(W)); + if constexpr (ComputeWeights) { + auto W = FECache.getSummaryEdgeFunction(SE, CL, Succ, Fct); + Outs.emplace_back(Fct, Succ, std::move(W)); + } else { + Outs.emplace_back(Fct, Succ, weight_type{}); + } } } } @@ -76,8 +101,12 @@ template class IDERuleProvider { auto Facts = FECache.getNormalFlowFunction(SE, Succ)->computeTargets(CL); for (auto &Fct : Facts) { - auto W = FECache.getNormalEdgeFunction(SE, CL, Succ, Fct); - Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + if constexpr (ComputeWeights) { + auto W = FECache.getNormalEdgeFunction(SE, CL, Succ, Fct); + Outs.emplace_back(std::move(Fct), Succ, std::move(W)); + } else { + Outs.emplace_back(std::move(Fct), Succ, weight_type{}); + } } } } @@ -109,7 +138,13 @@ template class IDERuleProvider { auto Facts = FECache.getCallFlowFunction(SE, DestFun)->computeTargets(CL); auto EntrySEs = ICF->getStartPointsOf(DestFun); for (auto &&Fct : Facts) { - auto W = FECache.getCallEdgeFunction(SE, CL, DestFun, Fct); + auto W = [&] { + if constexpr (ComputeWeights) { + return FECache.getCallEdgeFunction(SE, CL, DestFun, Fct); + } else { + return weight_type{}; + } + }(); for (const auto &EntrySE : EntrySEs) { for (const auto &Succ : RetSites) { Outs.emplace_back(Fct, Succ, EntrySE, W); @@ -151,9 +186,13 @@ template class IDERuleProvider { auto Facts = FECache.getRetFlowFunction(CS, DestFun, ExitSE, RetSiteSE) ->computeTargets(CL); for (auto &Fct : Facts) { - auto W = FECache.getReturnEdgeFunction(CS, DestFun, ExitSE, CL, - RetSiteSE, Fct); - Outs.emplace_back(std::move(Fct), std::move(W)); + if constexpr (ComputeWeights) { + auto W = FECache.getReturnEdgeFunction(CS, DestFun, ExitSE, CL, + RetSiteSE, Fct); + Outs.emplace_back(std::move(Fct), std::move(W)); + } else { + Outs.emplace_back(std::move(Fct), weight_type{}); + } } } @@ -167,19 +206,24 @@ template class IDERuleProvider { for (const auto &[Inst, Facts] : Problem->initialSeeds().getSeeds()) { for (const auto &[Fact, Val] : Facts) { - if (Val == Problem->topElement()) { - continue; - } - if (Val == Problem->bottomElement()) { - if constexpr (HasJoinLatticeTraits) { - Outs.emplace_back(Fact, Inst, AllBottom{}); + if constexpr (ComputeWeights) { + using l_t = typename ProblemT::l_t; + if (Val == Problem->topElement()) { + continue; + } + if (Val == Problem->bottomElement()) { + if constexpr (HasJoinLatticeTraits) { + Outs.emplace_back(Fact, Inst, AllBottom{}); + } else { + Outs.emplace_back(Fact, Inst, AllBottom{Val}); + } } else { - Outs.emplace_back(Fact, Inst, AllBottom{Val}); + Outs.emplace_back( + Fact, Inst, + ConstantEdgeFunction{NonTopBotValue::unwrap(Val)}); } } else { - Outs.emplace_back( - Fact, Inst, - ConstantEdgeFunction{NonTopBotValue::unwrap(Val)}); + Outs.emplace_back(Fact, Inst, weight_type{}); } } } @@ -187,7 +231,7 @@ template class IDERuleProvider { return Outs; } - [[nodiscard]] constexpr auto &ideProblem() const noexcept { return *Problem; } + [[nodiscard]] constexpr auto &problem() const noexcept { return *Problem; } private: ProblemT *Problem{}; @@ -196,4 +240,11 @@ template class IDERuleProvider { FlowEdgeFunctionCache FECache{ *Problem}; }; + +template +using IDERuleProvider = IfdsIdeRuleProvider; + +template +using IFDSRuleProvider = IfdsIdeRuleProvider; + } // namespace psr::wpds diff --git a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp index 5e25dd4de0..c72be0aaaf 100644 --- a/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp +++ b/unittests/PhasarLLVM/DataFlow/IfdsIde/WPDSSolverTest.cpp @@ -2,8 +2,7 @@ #include "phasar/DataFlow/WPDS/Solver/WPDSSolver.h" #include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h" -#include "phasar/DataFlow/WPDS/IDERuleProvider.h" -#include "phasar/DataFlow/WPDS/IFDSRuleProvider.h" +#include "phasar/DataFlow/WPDS/IfdsIdeRuleProvider.h" #include "phasar/Domain/LatticeDomain.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -13,6 +12,7 @@ #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Printer.h" #include "phasar/Utils/SemiRing.h" +#include "phasar/Utils/Timer.h" #include "llvm/IR/IntrinsicInst.h" @@ -30,24 +30,22 @@ class WPDSSolverTest : public ::testing::TestWithParam { static constexpr auto PathToLlFiles = PHASAR_BUILD_SUBFOLDER("linear_constant/"); - auto doWPDSAnalysis(auto &Problem, auto &ICFG) { - wpds::IDERuleProvider LCARules(&Problem, &ICFG); - WPDSSolver NewSolver(&LCARules, &Problem); - auto Start = std::chrono::steady_clock::now(); - NewSolver.solve(); - auto End = std::chrono::steady_clock::now(); - llvm::errs() << "WPDSSolver Elapsed:\t\t" << (End - Start).count() - << "ns\n"; - return NewSolver.consumeSolverResults(); - } - auto doPDSAnalysis(auto &Problem, auto &ICFG) { - wpds::IFDSRuleProvider LCARules(&Problem, &ICFG); - WPDSSolver NewSolver(&LCARules, &BinarySemiRing::Instance); - auto Start = std::chrono::steady_clock::now(); - NewSolver.solve(); - auto End = std::chrono::steady_clock::now(); - llvm::errs() << "PDSSolver Elapsed:\t\t" << (End - Start).count() << "ns\n"; - return NewSolver.consumeSolverResults(); + template + auto doWPDSAnalysis(auto &Problem, auto &ICFG, + std::bool_constant ComputeWeights) { + wpds::IfdsIdeRuleProvider LCARules(&Problem, &ICFG, ComputeWeights); + Timer Tm([](auto Elapsed) { + llvm::errs() << "WPDSSolver Elapsed:\t\t" << Elapsed.count() << "ns\n"; + }); + if constexpr (DoIDEAnalysis) { + WPDSSolver NewSolver(&LCARules, &Problem); + NewSolver.solve(); + return NewSolver.consumeSolverResults(); + } else { + WPDSSolver NewSolver(&LCARules, &BinarySemiRing::Instance); + NewSolver.solve(); + return NewSolver.consumeSolverResults(); + } } template @@ -68,13 +66,8 @@ class WPDSSolverTest : public ::testing::TestWithParam { llvm::errs() << "IterativeIDESolver Elapsed:\t" << BaselineTime.count() << "ns\n"; - auto NewResults = [&] { - if constexpr (DoIDEAnalysis) { - return doWPDSAnalysis(Problem, ICFG); - } else { - return doPDSAnalysis(Problem, ICFG); - } - }(); + auto NewResults = + doWPDSAnalysis(Problem, ICFG, std::bool_constant{}); if (PrintDump) { BaselineSolver.dumpResults();