Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDE Solver Strategy #669

Open
wants to merge 16 commits into
base: development
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Checks: '-*,
misc-*,
-misc-non-private-member-variables-in-classes,
-misc-no-recursion,
-misc-use-anonymous-namespace,
readability-*,
-readability-function-cognitive-complexity,
-readability-else-after*,
Expand Down
6 changes: 6 additions & 0 deletions include/phasar/ControlFlow/ICFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ template <typename Derived> class ICFGBase {
return self().getAllFunctionsImpl();
}

/// Returns the number of total functions that were considered when building
/// up this ICFG. Equals the size of getAllFunctions()
[[nodiscard]] size_t getNumFunctions() const noexcept {
return self().getNumFunctionsImpl();
}

/// returns the function definition or declaration with the given name. If
/// ther eis no such function, returns a default constructed f_t (nullptr for
/// pointers).
Expand Down
2 changes: 0 additions & 2 deletions include/phasar/DataFlow.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,10 @@
#include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h"
#include "phasar/DataFlow/IfdsIde/IFDSTabulationProblem.h"
#include "phasar/DataFlow/IfdsIde/InitialSeeds.h"
#include "phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h"
#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h"
#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h"
#include "phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h"
#include "phasar/DataFlow/IfdsIde/Solver/PathAwareIDESolver.h"
#include "phasar/DataFlow/IfdsIde/Solver/PathEdge.h"
#include "phasar/DataFlow/IfdsIde/SolverResults.h"
#include "phasar/DataFlow/IfdsIde/SpecialSummaries.h"
#include "phasar/DataFlow/Mono/Contexts/CallStringCTX.h"
Expand Down
4 changes: 2 additions & 2 deletions include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,8 @@ template <typename L, typename = std::enable_if_t<
}

template <typename L>
[[nodiscard]] llvm::raw_ostream &
operator<<(llvm::raw_ostream &OS, ByConstRef<ConstantEdgeFunction<L>> Id) {
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS,
const ConstantEdgeFunction<L> &Id) {
OS << "ConstantEF";
if constexpr (is_llvm_printable_v<
typename ConstantEdgeFunction<L>::value_type>) {
Expand Down
6 changes: 3 additions & 3 deletions include/phasar/DataFlow/IfdsIde/InitialSeeds.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ template <typename N, typename D, typename L> class InitialSeeds {
for (const auto &[Node, Facts] : Seeds) {
OS << "At ";
printNode(Node);
OS << "\n";
OS << '\n';
for (const auto &[Fact, Value] : Facts) {
OS << "> ";
printFact(Fact);
OS << " --> \\." << Value << "\n";
OS << " --> \\." << Value << '\n';
}
OS << "\n";
OS << '\n';
}
OS << "========================== End Seeds ==========================\n";
}
Expand Down
256 changes: 256 additions & 0 deletions include/phasar/DataFlow/IfdsIde/Solver/EagerIDESolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,256 @@
/******************************************************************************
* Copyright (c) 2023 Fabian Schiebel.
* All rights reserved. This program and the accompanying materials are made
* available under the terms of LICENSE.txt.
*
* Contributors:
* Fabian Schiebel and others
*****************************************************************************/

#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_EAGERIDESOLVER_H
#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_EAGERIDESOLVER_H

#include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h"
#include "phasar/DataFlow/IfdsIde/Solver/detail/IDESolverImpl.h"

namespace psr {

/// Solves the given IDETabulationProblem as described in the 1996 paper by
/// Sagiv, Horwitz and Reps. To solve the problem, call solve(). Results
/// can then be queried by using resultAt() and resultsAt().
///
/// Propagates data-flow facts onto the statement, where they were generated.
template <typename AnalysisDomainTy, typename Container>
class IDESolver<AnalysisDomainTy, Container, PropagateOntoStrategy>
: public IDESolverImpl<
IDESolver<AnalysisDomainTy, Container, PropagateOntoStrategy>,
AnalysisDomainTy, Container, PropagateOntoStrategy> {
using base_t = IDESolverImpl<
IDESolver<AnalysisDomainTy, Container, PropagateOntoStrategy>,
AnalysisDomainTy, Container, PropagateOntoStrategy>;

public:
using ProblemTy = IDETabulationProblem<AnalysisDomainTy, Container>;
using container_type = typename ProblemTy::container_type;
using FlowFunctionPtrType = typename ProblemTy::FlowFunctionPtrType;

using l_t = typename AnalysisDomainTy::l_t;
using n_t = typename AnalysisDomainTy::n_t;
using i_t = typename AnalysisDomainTy::i_t;
using d_t = typename AnalysisDomainTy::d_t;
using f_t = typename AnalysisDomainTy::f_t;
using t_t = typename AnalysisDomainTy::t_t;
using v_t = typename AnalysisDomainTy::v_t;

explicit IDESolver(IDETabulationProblem<AnalysisDomainTy, Container> &Problem,
const i_t *ICF, PropagateOntoStrategy Strategy = {})
: base_t(Problem, ICF, Strategy) {}

private:
friend base_t;
friend IDESolverAPIMixin<
IDESolver<AnalysisDomainTy, Container, PropagateOntoStrategy>>;

/// -- Phase I customization

bool updateJumpFunction(d_t SourceVal, n_t Target, d_t TargetVal,
EdgeFunction<l_t> *f) {
EdgeFunction<l_t> JumpFnE = [&]() {
const auto RevLookupResult =
this->JumpFn->reverseLookup(Target, TargetVal);
if (RevLookupResult) {
const auto &JumpFnContainer = RevLookupResult->get();
const auto Find = std::find_if(
JumpFnContainer.begin(), JumpFnContainer.end(),
[SourceVal](auto &KVpair) { return KVpair.first == SourceVal; });
if (Find != JumpFnContainer.end()) {
return Find->second;
}
}
// jump function is initialized to all-top if no entry
// was found
return this->AllTop;
}();

EdgeFunction<l_t> fPrime = JumpFnE.joinWith(*f);
bool NewFunction = fPrime != JumpFnE;
if (NewFunction) {
IF_LOG_LEVEL_ENABLED(DEBUG, {
PHASAR_LOG_LEVEL(
DEBUG, "Join: " << JumpFnE << " & " << *f
<< (JumpFnE == *f ? " (EF's are equal)" : ""));
PHASAR_LOG_LEVEL(
DEBUG, " = " << f << (NewFunction ? " (new jump func)" : ""));
PHASAR_LOG_LEVEL(DEBUG, ' ');
});

*f = fPrime;
this->JumpFn->addFunction(std::move(SourceVal), std::move(Target),
std::move(TargetVal), std::move(fPrime));

IF_LOG_LEVEL_ENABLED(
DEBUG, if (!this->IDEProblem->isZeroValue(TargetVal)) {
PHASAR_LOG_LEVEL(DEBUG,
"EDGE: <F: "
<< FToString(this->ICF->getFunctionOf(Target))
<< ", D: " << DToString(SourceVal) << '>');
PHASAR_LOG_LEVEL(DEBUG, " ---> <N: " << NToString(Target) << ',');
PHASAR_LOG_LEVEL(DEBUG,
" D: " << DToString(TargetVal) << ',');
PHASAR_LOG_LEVEL(DEBUG, " EF: " << fPrime << '>');
PHASAR_LOG_LEVEL(DEBUG, ' ');
});
}
return NewFunction;
}

template <typename TargetsT>
void updateWithNewEdges(d_t SourceVal, n_t OldTarget, n_t /*NewTarget*/,
const TargetsT &NewTargets, d_t TargetVal,
EdgeFunction<l_t> EF) {
if (updateJumpFunction(SourceVal, OldTarget, TargetVal, &EF)) {
auto It = NewTargets.begin();
auto End = NewTargets.end();
if (It == End) {
return;
}

auto Next = std::next(It);
if (Next == End) {
addWorklistItem(SourceVal, *It, std::move(TargetVal), std::move(EF));
return;
}

for (; It != End; ++It) {
addWorklistItem(SourceVal, *It, TargetVal, EF);
}
}
}

const llvm::SmallVectorImpl<std::pair<d_t, EdgeFunction<l_t>>> *
incomingJumpFunctionsAtCall(
n_t CallSite, d_t TargetVal,
llvm::SmallVectorImpl<std::pair<d_t, EdgeFunction<l_t>>> &Storage) {
const auto &Preds = this->ICF->getPredsOf(CallSite);

if (Preds.size() == 1) {
auto Opt = this->JumpFn->reverseLookup(*Preds.begin(), TargetVal);
if (Opt) {
return &Opt->get();
}
return nullptr;
}

if (Preds.empty()) {
// We are at the start of the current function
Storage.emplace_back(std::move(TargetVal), EdgeIdentity<l_t>{});
return &Storage;
}

for (const auto &Pred : Preds) {
auto Opt = this->JumpFn->reverseLookup(Pred, TargetVal);
if (Opt) {
Storage.append(Opt->get());
}
}

return Storage.empty() ? nullptr : &Storage;
}

void addInitialWorklistItem(d_t SourceVal, n_t Target, d_t TargetVal,
EdgeFunction<l_t> EF) {
addWorklistItem(std::move(SourceVal), std::move(Target),
std::move(TargetVal), std::move(EF));
}

void addWorklistItem(d_t SourceVal, n_t Target, d_t TargetVal,
EdgeFunction<l_t> EF) {
WorkList.emplace_back(
PathEdge{std::move(SourceVal), std::move(Target), std::move(TargetVal)},
std::move(EF));
}

bool doNext() {
assert(!WorkList.empty());
auto [Edge, EF] = std::move(WorkList.back());
WorkList.pop_back();

this->propagate(std::move(Edge), std::move(EF));

return !WorkList.empty();
}

/// -- Phase II customization

void propagateValueAtStart(const std::pair<n_t, d_t> NAndD, n_t Stmt) {
PAMM_GET_INSTANCE;
d_t Fact = NAndD.second;
f_t Func = this->ICF->getFunctionOf(Stmt);
for (const n_t &CS : this->ICF->getCallsFromWithin(Func)) {
for (const auto &BeforeCS : this->ICF->getPredsOf(CS)) {
auto LookupResults = this->JumpFn->forwardLookup(Fact, BeforeCS);
if (!LookupResults) {
continue;
}
for (size_t I = 0; I < LookupResults->get().size(); ++I) {
auto Entry = LookupResults->get()[I];
d_t dPrime = Entry.first;
auto fPrime = Entry.second;
n_t SP = Stmt;
l_t Val = seedVal(SP, Fact);
INC_COUNTER("Value Propagation", 1, Full);
this->propagateValue(CS, dPrime, fPrime.computeTarget(Val));
}
}
}
}

std::vector<n_t> getAllValueComputationNodes() const {
std::vector<n_t> Ret;
Ret.reserve(this->ICF->getNumFunctions() * 2); // Just a rough guess

for (const auto &Fun : this->ICF->getAllFunctions()) {
for (const auto &Inst : this->ICF->getAllInstructionsOf(Fun)) {
Ret.push_back(Inst);
}
}
return Ret;
}

l_t seedVal(n_t NHashN, d_t NHashD) {
if (SeedValues.contains(NHashN, NHashD)) {
return SeedValues.get(NHashN, NHashD);
}
return this->IDEProblem->topElement();
}

void setSeedVal(n_t NHashN, d_t NHashD, l_t L) {
SeedValues.insert(std::move(NHashN), std::move(NHashD), std::move(L));
}

// -- Data members

std::vector<std::pair<PathEdge<n_t, d_t>, EdgeFunction<l_t>>> WorkList;
Table<n_t, d_t, l_t> SeedValues;
};

template <typename Problem, typename ICF>
IDESolver(Problem &, ICF *, PropagateOntoStrategy)
-> IDESolver<typename Problem::ProblemAnalysisDomain,
typename Problem::container_type, PropagateOntoStrategy>;

template <typename AnalysisDomainTy, typename Container>
OwningSolverResults<typename AnalysisDomainTy::n_t,
typename AnalysisDomainTy::d_t,
typename AnalysisDomainTy::l_t>
solveIDEProblem(IDETabulationProblem<AnalysisDomainTy, Container> &Problem,
const typename AnalysisDomainTy::i_t &ICF,
PropagateOntoStrategy Strategy) {
IDESolver Solver(Problem, &ICF, Strategy);
Solver.solve();
return Solver.consumeSolverResults();
}

} // namespace psr

#endif
Loading
Loading