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 diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index 555a69bf52..4564a2489b 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 @@ -180,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 @@ -203,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. @@ -215,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 @@ -248,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()) { @@ -276,49 +239,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 +282,24 @@ 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 { + 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 +1754,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..0e760878cb 100644 --- a/include/phasar/DataFlow/IfdsIde/SolverResults.h +++ b/include/phasar/DataFlow/IfdsIde/SolverResults.h @@ -19,60 +19,243 @@ #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 psr { +namespace llvm { +class Instruction; +class Value; +} // namespace llvm -template class SolverResults { -private: - Table &Results; - D ZV; +namespace psr { +// For sorting the results in dumpResults() +std::string getMetaDataID(const llvm::Value *V); +namespace detail { +template +class SolverResultsBase { public: - SolverResults(Table &ResTab, D ZV) : Results(ResTab), ZV(ZV) {} + using n_t = N; + using d_t = D; + using l_t = L; - ByConstRef 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); } - 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) { - for (auto It = Result.begin(); It != Result.end();) { - if (It->first == ZV) { - It = Result.erase(It); - } else { - ++It; - } - } + Result.erase(self().ZV); } return Result; } // this function only exists for IFDS problems which use BinaryDomain as their // value domain L - 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) { - 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 + 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 = self().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: + [[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; + +public: + 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 { + return {Results, ZV}; + } + operator SolverResults() && = delete; + +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"; }