Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
116 changes: 115 additions & 1 deletion include/phasar/DataFlow/IfdsIde/Solver/IdBasedSolverResults.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@

#include "phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverResults.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/Printer.h"
#include "phasar/Utils/Utilities.h"

#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallVector.h"

#include <cassert>
#include <cstddef>
#include <set>
#include <unordered_map>
#include <utility>

namespace llvm {
class Instruction;
class Value;
} // namespace llvm

namespace psr {

namespace detail {
Expand All @@ -31,7 +39,7 @@ class IdBasedSolverResultsBase {

// --v Needed for llvm::mapped_iterator
// NOLINTNEXTLINE(readability-const-return-type)
const std::pair<const d_t &, const l_t &>
const std::pair<ByConstRef<d_t>, ByConstRef<l_t>>
operator()(ByConstRef<typename row_map_t::value_type> Entry) const {
return {Results->FactCompressor[Entry.first],
Results->ValCompressor[Entry.second]};
Expand Down Expand Up @@ -81,6 +89,7 @@ class IdBasedSolverResultsBase {
}

[[nodiscard]] size_t size() const noexcept { return Row->size(); }
[[nodiscard]] bool empty() const noexcept { return Row->empty(); }

private:
const detail::IterativeIDESolverResults<n_t, d_t, l_t> *Results{};
Expand Down Expand Up @@ -174,6 +183,49 @@ class IdBasedSolverResultsBase {
}
}

/// 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.
[[nodiscard]] std::unordered_map<d_t, l_t>
resultsAtInLLVMSSA(ByConstRef<n_t> Stmt,
bool AllowOverapproximation = false) const
requires same_as_decay<std::remove_pointer_t<n_t>, llvm::Instruction>;

/// 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.
[[nodiscard]] l_t resultAtInLLVMSSA(ByConstRef<n_t> Stmt, d_t Value,
bool AllowOverapproximation = false) const
requires same_as_decay<std::remove_pointer_t<n_t>, llvm::Instruction>;

template <typename ICFGTy>
void dumpResults(const ICFGTy &ICF,
llvm::raw_ostream &OS = llvm::outs()) const;

private:
[[nodiscard]] constexpr const auto &results() const noexcept {
return static_cast<const Derived *>(this)->resultsImpl();
Expand Down Expand Up @@ -219,6 +271,17 @@ class OwningIdBasedSolverResults
assert(this->Results != nullptr);
}

[[nodiscard]] IdBasedSolverResults<N, D, L> get() const & noexcept {
return IdBasedSolverResults<N, D, L>{Results.get()};
}
IdBasedSolverResults<N, D, L> get() && = delete;

[[nodiscard]] operator IdBasedSolverResults<N, D, L>() const & noexcept {
return get();
}

operator IdBasedSolverResults<N, D, L>() && = delete;

private:
[[nodiscard]] constexpr const auto &resultsImpl() const noexcept {
assert(Results != nullptr);
Expand All @@ -227,6 +290,57 @@ class OwningIdBasedSolverResults

std::unique_ptr<const detail::IterativeIDESolverResults<N, D, L>> Results{};
};

// For sorting the results in dumpResults()
std::string getMetaDataID(const llvm::Value *V);

template <typename Derived, typename N, typename D, typename L>
template <typename ICFGTy>
void detail::IdBasedSolverResultsBase<Derived, N, D, L>::dumpResults(
const ICFGTy &ICF, llvm::raw_ostream &OS) const {
using f_t = typename ICFGTy::f_t;

auto ResultEntries = llvm::to_vector(getAllResultEntries());

std::ranges::sort(ResultEntries, [](const auto &Lhs, const auto &Rhs) {
const auto &LRow = std::get<0>(Lhs);
const auto &RRow = std::get<0>(Rhs);
if constexpr (std::is_same_v<n_t, const llvm::Instruction *>) {
return StringIDLess{}(getMetaDataID(LRow), getMetaDataID(RRow));
} else {
// If non-LLVM IR is used
return LRow < RRow;
}
});

OS << "\n***************************************************************\n"
<< "* Raw IterativeIDESolver results *\n"
<< "***************************************************************\n";

f_t PrevFn = f_t{};
f_t CurrFn = f_t{};

for (const auto &[Row, Column] : ResultEntries) {
if (Column.empty()) {
continue;
}
CurrFn = ICF.getFunctionOf(Row);
if (PrevFn != CurrFn) {
PrevFn = CurrFn;
OS << "\n\n============ Results for function '" +
ICF.getFunctionName(CurrFn) + "' ============\n";
}

std::string NString = NToString(Row);
std::string Line(NString.size(), '-');
OS << "\n\nN: " << NString << "\n---" << Line << '\n';

for (const auto &[Col, Val] : Column) {
OS << "\tD: " << DToString(Col) << " | V: " << LToString(Val) << '\n';
}
}
OS << '\n';
}
} // namespace psr

#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_SOLVER_IDBASEDSOLVERRESULTS_H
49 changes: 1 addition & 48 deletions include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,54 +154,7 @@ class IterativeIDESolver
}

void dumpResults(llvm::raw_ostream &OS = llvm::outs()) const {
OS << "\n***************************************************************\n"
<< "* Raw IDESolver results *\n"
<< "***************************************************************\n";
auto Cells = this->base_results_t::ValTab_cellVec();
if (Cells.empty()) {
OS << "No results computed!\n";
return;
}

std::sort(Cells.begin(), Cells.end(), [](const auto &Lhs, const auto &Rhs) {
if constexpr (std::is_same_v<n_t, const llvm::Instruction *>) {
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 Curr{};
f_t PrevFn{};
f_t CurrFn{};

for (const auto &Cell : Cells) {
Curr = Cell.getRowKey();
CurrFn = ICFG.getFunctionOf(Curr);
if (PrevFn != CurrFn) {
PrevFn = CurrFn;
OS << "\n\n============ Results for function '" +
ICFG.getFunctionName(CurrFn) + "' ============\n";
}
if (Prev != Curr) {
Prev = Curr;
std::string NString = NToString(Curr);
std::string Line(NString.size(), '-');

OS << "\n\nN: " << NString << "\n---" << Line << '\n';
}
OS << "\tD: " << DToString(Cell.getColumnKey());
if constexpr (ComputeValues) {
OS << " | V: " << LToString(Cell.getValue());
}

OS << '\n';
}

OS << '\n';
getSolverResults().dumpResults(ICFG, OS);
}

[[nodiscard]] IterativeIDESolverStats getStats() const noexcept
Expand Down
Loading
Loading