From 8f4cdafa37161b235d8abbbdf6d35cc29340dadb Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 3 Apr 2023 17:28:32 +0200 Subject: [PATCH 1/3] Change solver-config default to not implicitly record the ESG edges anymore --- include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h index e402ada1cb..b3f9493641 100644 --- a/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h +++ b/include/phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h @@ -63,9 +63,8 @@ struct IFDSIDESolverConfig { const IFDSIDESolverConfig &SC); private: - SolverConfigOptions Options = SolverConfigOptions::AutoAddZero | - SolverConfigOptions::ComputeValues | - SolverConfigOptions::RecordEdges; + SolverConfigOptions Options = + SolverConfigOptions::AutoAddZero | SolverConfigOptions::ComputeValues; }; } // namespace psr From 13e2787fe31687a5f7a43cef796ab6036f22dd0d Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 3 Apr 2023 18:16:38 +0200 Subject: [PATCH 2/3] Allow moving the SolverResults out of the IDESolver --- .../DataFlow/IfdsIde/Solver/IDESolver.h | 84 ++++----- .../DataFlow/IfdsIde/Solver/IFDSSolver.h | 26 ++- .../phasar/DataFlow/IfdsIde/SolverResults.h | 162 ++++++++++++++++-- include/phasar/Utils/Printer.h | 15 +- tools/example-tool/myphasartool.cpp | 7 +- 5 files changed, 211 insertions(+), 83 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 555a69bf52..6614f30507 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -50,14 +50,7 @@ #include #include -namespace llvm { -class Instruction; -class Value; -} // namespace llvm - namespace psr { -// For sorting the results in dumpResults() -std::string getMetaDataID(const llvm::Value *V); /// Solves the given IDETabulationProblem as described in the 1996 paper by /// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results @@ -276,49 +269,7 @@ class IDESolver { } void dumpResults(llvm::raw_ostream &OS = llvm::outs()) { - PAMM_GET_INSTANCE; - START_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); - OS << "\n***************************************************************\n" - << "* Raw IDESolver results *\n" - << "***************************************************************\n"; - auto Cells = this->ValTab.cellVec(); - if (Cells.empty()) { - OS << "No results computed!" << '\n'; - } else { - std::sort( - Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { - if constexpr (std::is_same_v) { - return StringIDLess{}(getMetaDataID(Lhs.getRowKey()), - getMetaDataID(Rhs.getRowKey())); - } else { - // If non-LLVM IR is used - return Lhs.getRowKey() < Rhs.getRowKey(); - } - }); - n_t Prev = n_t{}; - n_t Curr = n_t{}; - f_t PrevFn = f_t{}; - f_t CurrFn = f_t{}; - for (unsigned I = 0; I < Cells.size(); ++I) { - Curr = Cells[I].getRowKey(); - CurrFn = ICF->getFunctionOf(Curr); - if (PrevFn != CurrFn) { - PrevFn = CurrFn; - OS << "\n\n============ Results for function '" + - ICF->getFunctionName(CurrFn) + "' ============\n"; - } - if (Prev != Curr) { - Prev = Curr; - std::string NString = IDEProblem.NtoString(Curr); - std::string Line(NString.size(), '-'); - OS << "\n\nN: " << NString << "\n---" << Line << '\n'; - } - OS << "\tD: " << IDEProblem.DtoString(Cells[I].getColumnKey()) - << " | V: " << IDEProblem.LtoString(Cells[I].getValue()) << '\n'; - } - } - OS << '\n'; - STOP_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); + getSolverResults().dumpResults(*ICF, IDEProblem, OS); } void dumpAllInterPathEdges() { @@ -361,9 +312,25 @@ class IDESolver { } } - SolverResults getSolverResults() { - return SolverResults(this->ValTab, - IDEProblem.getZeroValue()); + /// Returns a view into the computed solver-results. + /// + /// NOTE: The SolverResults store a reference into this IDESolver, so its + /// lifetime is also bound to the lifetime of this solver. If you want to use + /// the solverResults beyond the lifetime of this solver, use + /// comsumeSolverResults() instead. + [[nodiscard]] SolverResults + getSolverResults() noexcept(std::is_nothrow_copy_constructible_v) { + return SolverResults(this->ValTab, ZeroValue); + } + + /// Moves the computed solver-results out of this solver such that the solver + /// can be destroyed without that the analysis results are lost. + /// Do not call any function (including getSolverResults()) on this IDESolver + /// instance after that. + [[nodiscard]] OwningSolverResults + consumeSolverResults() noexcept(std::is_nothrow_move_constructible_v) { + return OwningSolverResults(std::move(this->ValTab), + std::move(ZeroValue)); } protected: @@ -1818,6 +1785,17 @@ template using IDESolver_P = IDESolver; +template +OwningSolverResults +solveIDEProblem(IDETabulationProblem &Problem, + const typename AnalysisDomainTy::i_t &ICF) { + IDESolver Solver(Problem, &ICF); + Solver.solve(); + return Solver.consumeSolverResults(); +} + } // namespace psr #endif diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h index f9ed15d766..60f4bb892e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h @@ -28,8 +28,10 @@ namespace psr { -template -class IFDSSolver : public IDESolver> { +template > +class IFDSSolver + : public IDESolver, Container> { public: using ProblemTy = IFDSTabulationProblem; using d_t = typename AnalysisDomainTy::d_t; @@ -39,7 +41,8 @@ class IFDSSolver : public IDESolver> { template >> - IFDSSolver(IFDSTabulationProblem &IFDSProblem, const i_t *ICF) + IFDSSolver(IFDSTabulationProblem &IFDSProblem, + const i_t *ICF) : IDESolver>(IFDSProblem, ICF) {} virtual ~IFDSSolver() = default; @@ -96,10 +99,23 @@ class IFDSSolver : public IDESolver> { template IFDSSolver(Problem &, ICF *) - -> IFDSSolver; + -> IFDSSolver; template -using IFDSSolver_P = IFDSSolver; +using IFDSSolver_P = IFDSSolver; + +template +OwningSolverResults +solveIFDSProblem(IFDSTabulationProblem &Problem, + const typename AnalysisDomainTy::i_t &ICF) { + IFDSSolver Solver(Problem, &ICF); + Solver.solve(); + return Solver.consumeSolverResults(); +} } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 85f9aade7e..49dd7550a0 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -19,38 +19,45 @@ #include "phasar/Domain/BinaryDomain.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/Printer.h" #include "phasar/Utils/Table.h" +#include "phasar/Utils/Utilities.h" #include #include #include #include +namespace llvm { +class Instruction; +class Value; +} // namespace llvm + namespace psr { +// For sorting the results in dumpResults() +std::string getMetaDataID(const llvm::Value *V); template class SolverResults { -private: - Table &Results; - D ZV; - public: - SolverResults(Table &ResTab, D ZV) : Results(ResTab), ZV(ZV) {} + using n_t = N; + using d_t = D; + using l_t = L; + + SolverResults(Table &ResTab, + D ZV) noexcept(std::is_nothrow_move_constructible_v) + : Results(ResTab), ZV(std::move(ZV)) {} + SolverResults(Table &&ResTab, D ZV) = delete; - ByConstRef resultAt(ByConstRef Stmt, ByConstRef Node) const { + [[nodiscard]] L resultAt(ByConstRef Stmt, ByConstRef Node) const { return Results.get(Stmt, Node); } - std::unordered_map resultsAt(ByConstRef Stmt, - bool StripZero = false) const { + [[nodiscard]] std::unordered_map + resultsAt(ByConstRef Stmt, bool StripZero = false) const { std::unordered_map Result = Results.row(Stmt); if (StripZero) { - for (auto It = Result.begin(); It != Result.end();) { - if (It->first == ZV) { - It = Result.erase(It); - } else { - ++It; - } - } + Result.erase(ZV); } return Result; } @@ -60,7 +67,7 @@ template class SolverResults { template >> - std::set ifdsResultsAt(ByConstRef Stmt) const { + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { std::set KeySet; std::unordered_map ResultMap = this->resultsAt(Stmt); for (auto FlowFact : ResultMap) { @@ -73,6 +80,129 @@ template class SolverResults { getAllResultEntries() const { return Results.cellVec(); } + + template + void dumpResults(const ICFGTy &ICF, const NodePrinterBase &NP, + const DataFlowFactPrinterBase &DP, + const EdgeFactPrinterBase &LP, + llvm::raw_ostream &OS = llvm::outs()) { + using f_t = typename ICFGTy::f_t; + + PAMM_GET_INSTANCE; + START_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); + OS << "\n***************************************************************\n" + << "* Raw IDESolver results *\n" + << "***************************************************************\n"; + auto Cells = Results.cellVec(); + if (Cells.empty()) { + OS << "No results computed!" << '\n'; + } else { + std::sort( + Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) { + if constexpr (std::is_same_v) { + return StringIDLess{}(getMetaDataID(Lhs.getRowKey()), + getMetaDataID(Rhs.getRowKey())); + } else { + // If non-LLVM IR is used + return Lhs.getRowKey() < Rhs.getRowKey(); + } + }); + n_t Prev = n_t{}; + n_t Curr = n_t{}; + f_t PrevFn = f_t{}; + f_t CurrFn = f_t{}; + for (unsigned I = 0; I < Cells.size(); ++I) { + Curr = Cells[I].getRowKey(); + CurrFn = ICF.getFunctionOf(Curr); + if (PrevFn != CurrFn) { + PrevFn = CurrFn; + OS << "\n\n============ Results for function '" + + ICF.getFunctionName(CurrFn) + "' ============\n"; + } + if (Prev != Curr) { + Prev = Curr; + std::string NString = NP.NtoString(Curr); + std::string Line(NString.size(), '-'); + OS << "\n\nN: " << NString << "\n---" << Line << '\n'; + } + OS << "\tD: " << DP.DtoString(Cells[I].getColumnKey()) + << " | V: " << LP.LtoString(Cells[I].getValue()) << '\n'; + } + } + OS << '\n'; + STOP_TIMER("DFA IDE Result Dumping", PAMM_SEVERITY_LEVEL::Full); + } + + template + void dumpResults(const ICFGTy &ICF, const ProblemTy &IDEProblem, + llvm::raw_ostream &OS = llvm::outs()) { + dumpResults(ICF, IDEProblem, IDEProblem, IDEProblem, OS); + } + +private: + Table &Results; + D ZV; +}; + +template class OwningSolverResults { +public: + using n_t = N; + using d_t = D; + using l_t = L; + + OwningSolverResults(Table ResTab, + D ZV) noexcept(std::is_nothrow_move_constructible_v) + : Results(std::move(ResTab)), ZV(ZV) {} + + [[nodiscard]] operator SolverResults() const &noexcept( + std::is_nothrow_copy_constructible_v) { + return {Results, ZV}; + } + operator SolverResults() && = delete; + + [[nodiscard]] ByConstRef resultAt(ByConstRef Stmt, + ByConstRef Node) const { + return Results.get(Stmt, Node); + } + + [[nodiscard]] std::unordered_map + resultsAt(ByConstRef Stmt, bool StripZero = false) const { + return static_cast>(*this).resultsAt(Stmt, + StripZero); + } + + // this function only exists for IFDS problems which use BinaryDomain as their + // value domain L + template >> + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { + return static_cast>(*this).ifdsResultsAt(Stmt); + } + + [[nodiscard]] std::vector::Cell> + getAllResultEntries() const { + return Results.cellVec(); + } + + template + void dumpResults(const ICFGTy &ICF, const NodePrinterBase &NP, + const DataFlowFactPrinterBase &DP, + const ValuePrinter LP, + llvm::raw_ostream &OS = llvm::outs()) { + static_cast>(*this).dumpResults(ICF, NP, DP, LP, OS); + } + + template + void dumpResults(const ICFGTy &ICF, const ProblemTy &IDEProblem, + llvm::raw_ostream &OS = llvm::outs()) { + static_cast>(*this).dumpResults(ICF, IDEProblem, OS); + } + +private: + // psr::Table is not const-enabled, so we have to give out mutable references + mutable Table Results; + D ZV; }; } // namespace psr diff --git a/include/phasar/Utils/Printer.h b/include/phasar/Utils/Printer.h index c30e214010..fae88ba2fd 100644 --- a/include/phasar/Utils/Printer.h +++ b/include/phasar/Utils/Printer.h @@ -72,18 +72,21 @@ template struct TypePrinter { } }; -template struct EdgeFactPrinter { - using l_t = typename AnalysisDomainTy::l_t; +template struct EdgeFactPrinterBase { + using l_t = L; - virtual ~EdgeFactPrinter() = default; + virtual ~EdgeFactPrinterBase() = default; - virtual void printEdgeFact(llvm::raw_ostream &OS, l_t L) const = 0; + virtual void printEdgeFact(llvm::raw_ostream &OS, l_t Val) const = 0; - [[nodiscard]] std::string LtoString(l_t L) const { // NOLINT - return toStringBuilder(&EdgeFactPrinter::printEdgeFact, this, L); + [[nodiscard]] std::string LtoString(l_t Val) const { // NOLINT + return toStringBuilder(&EdgeFactPrinterBase::printEdgeFact, this, Val); } }; +template +using EdgeFactPrinter = EdgeFactPrinterBase; + template struct FunctionPrinter { using F = typename AnalysisDomainTy::f_t; diff --git a/tools/example-tool/myphasartool.cpp b/tools/example-tool/myphasartool.cpp index 9d82236d1c..fbfc269c62 100644 --- a/tools/example-tool/myphasartool.cpp +++ b/tools/example-tool/myphasartool.cpp @@ -57,9 +57,10 @@ int main(int Argc, const char **Argv) { llvm::outs() << "Testing IDE:\n"; auto M = createAnalysisProblem(HA, EntryPoints); - IDESolver T(M, &HA.getICFG()); - T.solve(); - T.dumpResults(); + // Alternative way of solving an IFDS/IDEProblem: + auto IDEResults = solveIDEProblem(M, HA.getICFG()); + IDEResults.dumpResults(HA.getICFG(), M); + } else { llvm::errs() << "error: file does not contain a 'main' function!\n"; } From 37844a9c55dbc37f4f9de1b200621635b610b673 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 3 Apr 2023 19:07:10 +0200 Subject: [PATCH 3/3] Move some more results code from IDESolver to SolverResults --- .../DataFlow/IfdsIde/Solver/IDESolver.h | 43 +--- .../phasar/DataFlow/IfdsIde/SolverResults.h | 187 +++++++++++------- 2 files changed, 126 insertions(+), 104 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 6614f30507..4564a2489b 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -173,8 +173,8 @@ class IDESolver { } /// Returns the L-type result for the given value at the given statement. - [[nodiscard]] virtual l_t resultAt(n_t Stmt, d_t Value) { - return ValTab.get(Stmt, Value); + [[nodiscard]] l_t resultAt(n_t Stmt, d_t Value) { + return getSolverResults().resultAt(Stmt, Value); } /// Returns the L-type result at the given statement for the given data-flow @@ -196,11 +196,7 @@ class IDESolver { [[nodiscard]] typename std::enable_if_t< std::is_same_v, llvm::Instruction *>, l_t> resultAtInLLVMSSA(NTy Stmt, d_t Value) { - if (Stmt->getType()->isVoidTy()) { - return ValTab.get(Stmt, Value); - } - assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); - return ValTab.get(Stmt->getNextNode(), Value); + return getSolverResults().resultAtInLLVMSSA(Stmt, Value); } /// Returns the resulting environment for the given statement. @@ -208,17 +204,7 @@ class IDESolver { /// TOP values are never returned. [[nodiscard]] virtual std::unordered_map resultsAt(n_t Stmt, bool StripZero = false) /*TODO const*/ { - std::unordered_map Result = ValTab.row(Stmt); - if (StripZero) { - for (auto It = Result.begin(); It != Result.end();) { - if (IDEProblem.isZeroValue(It->first)) { - It = Result.erase(It); - } else { - ++It; - } - } - } - return Result; + return getSolverResults().resultsAt(Stmt, StripZero); } /// Returns the data-flow results at the given statement while respecting @@ -241,23 +227,7 @@ class IDESolver { std::is_same_v, llvm::Instruction *>, std::unordered_map> resultsAtInLLVMSSA(NTy Stmt, bool StripZero = false) { - std::unordered_map Result = [this, Stmt]() { - if (Stmt->getType()->isVoidTy()) { - return ValTab.row(Stmt); - } - return ValTab.row(Stmt->getNextNode()); - }(); - if (StripZero) { - // TODO: replace with std::erase_if (C++20) - for (auto It = Result.begin(); It != Result.end();) { - if (IDEProblem.isZeroValue(It->first)) { - It = Result.erase(It); - } else { - ++It; - } - } - } - return Result; + return getSolverResults().resultsAtInLLVMSSA(Stmt, StripZero); } virtual void emitTextReport(llvm::raw_ostream &OS = llvm::outs()) { @@ -318,8 +288,7 @@ class IDESolver { /// lifetime is also bound to the lifetime of this solver. If you want to use /// the solverResults beyond the lifetime of this solver, use /// comsumeSolverResults() instead. - [[nodiscard]] SolverResults - getSolverResults() noexcept(std::is_nothrow_copy_constructible_v) { + [[nodiscard]] SolverResults getSolverResults() noexcept { return SolverResults(this->ValTab, ZeroValue); } diff --git a/include/phasar/DataFlow/IfdsIde/SolverResults.h b/include/phasar/DataFlow/IfdsIde/SolverResults.h index 49dd7550a0..0e760878cb 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -38,47 +38,107 @@ namespace psr { // For sorting the results in dumpResults() std::string getMetaDataID(const llvm::Value *V); -template class SolverResults { +namespace detail { +template +class SolverResultsBase { public: using n_t = N; using d_t = D; using l_t = L; - SolverResults(Table &ResTab, - D ZV) noexcept(std::is_nothrow_move_constructible_v) - : Results(ResTab), ZV(std::move(ZV)) {} - SolverResults(Table &&ResTab, D ZV) = delete; - - [[nodiscard]] L resultAt(ByConstRef Stmt, ByConstRef Node) const { - return Results.get(Stmt, Node); + [[nodiscard]] ByConstRef resultAt(ByConstRef Stmt, + ByConstRef Node) const { + return self().Results.get(Stmt, Node); } - [[nodiscard]] std::unordered_map - resultsAt(ByConstRef Stmt, bool StripZero = false) const { - std::unordered_map Result = Results.row(Stmt); + [[nodiscard]] std::unordered_map + resultsAt(ByConstRef Stmt, bool StripZero = false) const { + std::unordered_map Result = self().Results.row(Stmt); if (StripZero) { - Result.erase(ZV); + Result.erase(self().ZV); } return Result; } // this function only exists for IFDS problems which use BinaryDomain as their // value domain L - template >> - [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { + [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { std::set KeySet; - std::unordered_map ResultMap = this->resultsAt(Stmt); - for (auto FlowFact : ResultMap) { - KeySet.insert(FlowFact.first); + const auto &ResultMap = self().Results.row(Stmt); + for (const auto &[FlowFact, Val] : ResultMap) { + KeySet.insert(FlowFact); } return KeySet; } - [[nodiscard]] std::vector::Cell> + /// Returns the data-flow results at the given statement while respecting + /// LLVM's SSA semantics. + /// + /// An example: when a value is loaded and the location loaded from, here + /// variable 'i', is a data-flow fact that holds, then the loaded value '%0' + /// will usually be generated and also holds. However, due to the underlying + /// theory (and respective implementation) this load instruction causes the + /// loaded value to be generated and thus, it will be valid only AFTER the + /// load instruction, i.e., at the successor instruction. + /// + /// %0 = load i32, i32* %i, align 4 + /// + /// This result accessor function returns the results at the successor + /// instruction(s) reflecting that the expression on the left-hand side holds + /// if the expression on the right-hand side holds. + template + [[nodiscard]] typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + std::unordered_map> + resultsAtInLLVMSSA(ByConstRef Stmt, bool StripZero = false) { + std::unordered_map Result = [this, Stmt]() { + if (Stmt->getType()->isVoidTy()) { + return self().Results.row(Stmt); + } + assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); + return self().Results.row(Stmt->getNextNode()); + }(); + if (StripZero) { + Result.erase(self().ZV); + } + return Result; + } + + /// Returns the L-type result at the given statement for the given data-flow + /// fact while respecting LLVM's SSA semantics. + /// + /// An example: when a value is loaded and the location loaded from, here + /// variable 'i', is a data-flow fact that holds, then the loaded value '%0' + /// will usually be generated and also holds. However, due to the underlying + /// theory (and respective implementation) this load instruction causes the + /// loaded value to be generated and thus, it will be valid only AFTER the + /// load instruction, i.e., at the successor instruction. + /// + /// %0 = load i32, i32* %i, align 4 + /// + /// This result accessor function returns the results at the successor + /// instruction(s) reflecting that the expression on the left-hand side holds + /// if the expression on the right-hand side holds. + template + [[nodiscard]] typename std::enable_if_t< + std::is_same_v>, + llvm::Instruction>, + l_t> + resultAtInLLVMSSA(ByConstRef Stmt, d_t Value) { + if (Stmt->getType()->isVoidTy()) { + return self().Results.get(Stmt, Value); + } + assert(Stmt->getNextNode() && "Expected to find a valid successor node!"); + return self().Results.get(Stmt->getNextNode(), Value); + } + + [[nodiscard]] std::vector::Cell> getAllResultEntries() const { - return Results.cellVec(); + return self().Results.cellVec(); } template @@ -93,7 +153,7 @@ template class SolverResults { OS << "\n***************************************************************\n" << "* Raw IDESolver results *\n" << "***************************************************************\n"; - auto Cells = Results.cellVec(); + auto Cells = self().Results.cellVec(); if (Cells.empty()) { OS << "No results computed!" << '\n'; } else { @@ -140,65 +200,58 @@ template class SolverResults { } private: - Table &Results; - D ZV; + [[nodiscard]] Derived &self() noexcept { + static_assert(std::is_base_of_v); + return static_cast(*this); + } + [[nodiscard]] const Derived &self() const noexcept { + static_assert(std::is_base_of_v); + return static_cast(*this); + } }; +} // namespace detail + +template +class SolverResults + : public detail::SolverResultsBase, N, D, L> { + using base_t = detail::SolverResultsBase, N, D, L>; + friend base_t; -template class OwningSolverResults { public: - using n_t = N; - using d_t = D; - using l_t = L; + using typename base_t::d_t; + using typename base_t::l_t; + using typename base_t::n_t; + + SolverResults(Table &ResTab, ByConstRef ZV) noexcept + : Results(ResTab), ZV(ZV) {} + SolverResults(Table &&ResTab, ByConstRef ZV) = delete; + +private: + Table &Results; + ByConstRef ZV; +}; + +template +class OwningSolverResults + : public detail::SolverResultsBase, N, D, L> { + using base_t = + detail::SolverResultsBase, N, D, L>; + friend base_t; + +public: + using typename base_t::d_t; + using typename base_t::l_t; + using typename base_t::n_t; OwningSolverResults(Table ResTab, D ZV) noexcept(std::is_nothrow_move_constructible_v) : Results(std::move(ResTab)), ZV(ZV) {} - [[nodiscard]] operator SolverResults() const &noexcept( - std::is_nothrow_copy_constructible_v) { + [[nodiscard]] operator SolverResults() const &noexcept { return {Results, ZV}; } operator SolverResults() && = delete; - [[nodiscard]] ByConstRef resultAt(ByConstRef Stmt, - ByConstRef Node) const { - return Results.get(Stmt, Node); - } - - [[nodiscard]] std::unordered_map - resultsAt(ByConstRef Stmt, bool StripZero = false) const { - return static_cast>(*this).resultsAt(Stmt, - StripZero); - } - - // this function only exists for IFDS problems which use BinaryDomain as their - // value domain L - template >> - [[nodiscard]] std::set ifdsResultsAt(ByConstRef Stmt) const { - return static_cast>(*this).ifdsResultsAt(Stmt); - } - - [[nodiscard]] std::vector::Cell> - getAllResultEntries() const { - return Results.cellVec(); - } - - template - void dumpResults(const ICFGTy &ICF, const NodePrinterBase &NP, - const DataFlowFactPrinterBase &DP, - const ValuePrinter LP, - llvm::raw_ostream &OS = llvm::outs()) { - static_cast>(*this).dumpResults(ICF, NP, DP, LP, OS); - } - - template - void dumpResults(const ICFGTy &ICF, const ProblemTy &IDEProblem, - llvm::raw_ostream &OS = llvm::outs()) { - static_cast>(*this).dumpResults(ICF, IDEProblem, OS); - } - private: // psr::Table is not const-enabled, so we have to give out mutable references mutable Table Results;