Skip to content

Commit

Permalink
Pull in the IDESolver++ (aka. IterativeIDESolver) (#733)
Browse files Browse the repository at this point in the history
* Pull in the IDESolver++ (aka. IterativeIDESolver)

The solver is set-up in JF_N configuration from the paper "Scaling Interprocedural Static Data-Flow Analysis to Large C/C++ Applications: An Experience Report"

* Some cleanup based on review

* edge fact printing

* more cleanup

* tracking issue for c++20

* minor cleanup

* Add alignment comment

* Apply review comments

* Remove confusing copyright notes + fix header guards for newly added files
  • Loading branch information
fabianbs96 authored Nov 10, 2024
1 parent 96f0758 commit 64a7233
Show file tree
Hide file tree
Showing 66 changed files with 5,182 additions and 409 deletions.
4 changes: 4 additions & 0 deletions include/phasar/ControlFlow/ICFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ template <typename Derived> class ICFGBase {
return self().getAsJsonImpl();
}

[[nodiscard]] size_t getNumCallSites() const noexcept {
return self().getNumCallSitesImpl();
}

private:
const Derived &self() const noexcept {
return static_cast<const Derived &>(*this);
Expand Down
1 change: 1 addition & 0 deletions include/phasar/DataFlow/IfdsIde/EdgeFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/EmptyBaseOptimizationUtils.h"
#include "phasar/Utils/TypeTraits.h"

#include "llvm/ADT/DenseMapInfo.h"
Expand Down
108 changes: 108 additions & 0 deletions include/phasar/DataFlow/IfdsIde/GenericFlowFunction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#ifndef PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H
#define PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H

#include "phasar/DataFlow/IfdsIde/FlowFunctions.h"

namespace psr {
/// Encapsulates an unmanaged pointer to a FlowFunction
template <typename D, typename Container = std::set<D>>
class GenericFlowFunctionView {
public:
using FlowFunctionType = FlowFunction<D, Container>;
using FlowFunctionPtrType = std::unique_ptr<FlowFunctionType>;

using container_type = Container;
using value_type = typename container_type::value_type;

GenericFlowFunctionView() noexcept = default;
GenericFlowFunctionView(FlowFunctionType *FF) noexcept : FF(FF) {}

GenericFlowFunctionView(const GenericFlowFunctionView &) noexcept = default;
GenericFlowFunctionView &
operator=(const GenericFlowFunctionView &) noexcept = default;

~GenericFlowFunctionView() = default;

[[nodiscard]] container_type computeTargets(D Source) const {
assert(FF != nullptr);
return FF->computeTargets(std::move(Source));
}

explicit operator bool() const noexcept { return FF; }

[[nodiscard]] bool operator==(GenericFlowFunctionView Other) const noexcept {
return FF == Other.FF;
}
[[nodiscard]] bool operator==(std::nullptr_t) const noexcept {
return FF == nullptr;
}
[[nodiscard]] bool operator!=(GenericFlowFunctionView Other) const noexcept {
return !(*this == Other);
}
[[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return FF; }

private:
FlowFunctionType *FF = nullptr;
};

/// Encapsulates a managed pointer to a FlowFunction
template <typename D, typename Container = std::set<D>>
class GenericFlowFunction {
public:
using FlowFunctionType = FlowFunction<D, Container>;
using FlowFunctionPtrType = typename FlowFunctionType::FlowFunctionPtrType;

using container_type = Container;
using value_type = typename container_type::value_type;

GenericFlowFunction() noexcept = default;
GenericFlowFunction(FlowFunctionPtrType FF) noexcept : FF(std::move(FF)) {}
template <typename T, typename = std::enable_if_t<std::is_base_of_v<
FlowFunctionType, std::decay_t<T>>>>
GenericFlowFunction(T &&FF)
: FF(std::make_unique<std::decay_t<T>>(std::forward<T>(FF))) {}

template <typename T, typename... ArgTys>
explicit GenericFlowFunction(std::in_place_type_t<T> /*unused*/,
ArgTys &&...Args)
: FF(std::make_unique<T>(std::forward<ArgTys>(Args)...)) {}

GenericFlowFunction(GenericFlowFunction &&) noexcept = default;
GenericFlowFunction &operator=(GenericFlowFunction &&) noexcept = default;

GenericFlowFunction(const GenericFlowFunction &) = delete;
GenericFlowFunction &operator=(const GenericFlowFunction &) = delete;

~GenericFlowFunction() = default;

[[nodiscard]] container_type computeTargets(D Source) const {
assert(FF != nullptr);
return FF->computeTargets(std::move(Source));
}

explicit operator bool() const noexcept { return FF; }

operator GenericFlowFunctionView<D, Container>() const noexcept {
return FF.get();
}

[[nodiscard]] bool
operator==(GenericFlowFunctionView<D, Container> Other) const noexcept {
return FF == Other.FF;
}
[[nodiscard]] bool operator==(std::nullptr_t) const noexcept {
return FF == nullptr;
}
[[nodiscard]] bool
operator!=(GenericFlowFunctionView<D, Container> Other) const noexcept {
return !(*this == Other);
}
[[nodiscard]] bool operator!=(std::nullptr_t) const noexcept { return FF; }

private:
FlowFunctionPtrType FF;
};

} // namespace psr

#endif // PHASAR_PHASARLLVM_DATAFLOWSOLVER_IFDSIDE_GENERICFLOWFUNCTION_H
7 changes: 5 additions & 2 deletions include/phasar/DataFlow/IfdsIde/IDETabulationProblem.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "phasar/DataFlow/IfdsIde/FlowFunctions.h"
#include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h"
#include "phasar/DataFlow/IfdsIde/InitialSeeds.h"
#include "phasar/DataFlow/IfdsIde/Solver/GenericSolverResults.h"
#include "phasar/DataFlow/IfdsIde/SolverResults.h"
#include "phasar/Utils/JoinLattice.h"
#include "phasar/Utils/NullAnalysisPrinter.h"
Expand Down Expand Up @@ -134,15 +135,15 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
/// Generates a text report of the results that is written to the specified
/// output stream.
virtual void
emitTextReport([[maybe_unused]] const SolverResults<n_t, d_t, l_t> &Results,
emitTextReport([[maybe_unused]] GenericSolverResults<n_t, d_t, l_t> Results,
llvm::raw_ostream &OS = llvm::outs()) {
OS << "No text report available!\n";
}

/// Generates a graphical report, e.g. in html or other markup languages, of
/// the results that is written to the specified output stream.
virtual void emitGraphicalReport(
[[maybe_unused]] const SolverResults<n_t, d_t, l_t> &Results,
[[maybe_unused]] GenericSolverResults<n_t, d_t, l_t> Results,
llvm::raw_ostream &OS = llvm::outs()) {
OS << "No graphical report available!\n";
}
Expand All @@ -151,6 +152,8 @@ class IDETabulationProblem : public FlowFunctions<AnalysisDomainTy, Container>,
/// the level of soundness is ignored. Otherwise, true.
virtual bool setSoundness(Soundness /*S*/) { return false; }

const ProjectIRDBBase<db_t> *getProjectIRDB() const noexcept { return IRDB; }

protected:
typename FlowFunctions<AnalysisDomainTy, Container>::FlowFunctionPtrType
generateFromZero(d_t FactToGenerate) {
Expand Down
191 changes: 191 additions & 0 deletions include/phasar/DataFlow/IfdsIde/Solver/Compressor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H
#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H

#include "phasar/DB/ProjectIRDBBase.h"
#include "phasar/Utils/ByRef.h"
#include "phasar/Utils/TypeTraits.h"

#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/DenseMapInfo.h"
#include "llvm/ADT/SmallVector.h"

#include <cstdint>
#include <deque>
#include <functional>
#include <optional>
#include <type_traits>

namespace psr {
template <typename T, typename Enable = void> class Compressor;

template <typename T>
class Compressor<T, std::enable_if_t<CanEfficientlyPassByValue<T>>> {
public:
void reserve(size_t Capacity) {
assert(Capacity <= UINT32_MAX);
ToInt.reserve(Capacity);
FromInt.reserve(Capacity);
}

uint32_t getOrInsert(T Elem) {
auto [It, Inserted] = ToInt.try_emplace(Elem, ToInt.size());
if (Inserted) {
FromInt.push_back(Elem);
}
return It->second;
}

std::optional<uint32_t> getOrNull(T Elem) const {
if (auto It = ToInt.find(Elem); It != ToInt.end()) {
return It->second;
}
return std::nullopt;
}

T operator[](size_t Idx) const noexcept {
assert(Idx < FromInt.size());
return FromInt[Idx];
}

[[nodiscard]] size_t size() const noexcept { return FromInt.size(); }
[[nodiscard]] size_t capacity() const noexcept {
return FromInt.capacity() +
ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type);
}

auto begin() const noexcept { return FromInt.begin(); }
auto end() const noexcept { return FromInt.end(); }

private:
llvm::DenseMap<T, uint32_t> ToInt;
llvm::SmallVector<T, 0> FromInt;
};

template <typename T>
class Compressor<T, std::enable_if_t<!CanEfficientlyPassByValue<T>>> {
public:
void reserve(size_t Capacity) {
assert(Capacity <= UINT32_MAX);
ToInt.reserve(Capacity);
}

uint32_t getOrInsert(const T &Elem) {
if (auto It = ToInt.find(&Elem); It != ToInt.end()) {
return It->second;
}
auto Ret = FromInt.size();
auto *Ins = &FromInt.emplace_back(Elem);
ToInt[Ins] = Ret;
return Ret;
}

uint32_t getOrInsert(T &&Elem) {
if (auto It = ToInt.find(&Elem); It != ToInt.end()) {
return It->second;
}
auto Ret = FromInt.size();
auto *Ins = &FromInt.emplace_back(std::move(Elem));
ToInt[Ins] = Ret;
return Ret;
}

std::optional<uint32_t> getOrNull(const T &Elem) const {
if (auto It = ToInt.find(&Elem); It != ToInt.end()) {
return It->second;
}
return std::nullopt;
}

const T &operator[](size_t Idx) const noexcept {
assert(Idx < FromInt.size());
return FromInt[Idx];
}

[[nodiscard]] size_t size() const noexcept { return FromInt.size(); }
[[nodiscard]] size_t capacity() const noexcept {
return FromInt.size() +
ToInt.getMemorySize() / sizeof(typename decltype(ToInt)::value_type);
}

auto begin() const noexcept { return FromInt.begin(); }
auto end() const noexcept { return FromInt.end(); }

private:
struct DSI : llvm::DenseMapInfo<const T *> {
static auto getHashValue(const T *Elem) noexcept {
assert(Elem != nullptr);
if constexpr (has_llvm_dense_map_info<T>) {
return llvm::DenseMapInfo<T>::getHashValue(*Elem);
} else {
return std::hash<T>{}(*Elem);
}
}
static auto isEqual(const T *LHS, const T *RHS) noexcept {
if (LHS == RHS) {
return true;
}
if (LHS == DSI::getEmptyKey() || LHS == DSI::getTombstoneKey() ||
RHS == DSI::getEmptyKey() || RHS == DSI::getTombstoneKey()) {
return false;
}
if constexpr (has_llvm_dense_map_info<T>) {
return llvm::DenseMapInfo<T>::isEqual(*LHS, *RHS);
} else {
return *LHS == *RHS;
}
}
};

std::deque<T> FromInt;
llvm::DenseMap<const T *, uint32_t, DSI> ToInt;
};

struct NoneCompressor final {
constexpr NoneCompressor() noexcept = default;

template <typename T,
typename = std::enable_if_t<!std::is_same_v<NoneCompressor, T>>>
constexpr NoneCompressor(const T & /*unused*/) noexcept {}

template <typename T>
[[nodiscard]] decltype(auto) getOrInsert(T &&Val) const noexcept {
return std::forward<T>(Val);
}
template <typename T>
[[nodiscard]] decltype(auto) operator[](T &&Val) const noexcept {
return std::forward<T>(Val);
}
void reserve(size_t /*unused*/) const noexcept {}

[[nodiscard]] size_t size() const noexcept { return 0; }
[[nodiscard]] size_t capacity() const noexcept { return 0; }
};

class LLVMProjectIRDB;

/// Once we have fast instruction IDs (as we already have in IntelliSecPhasar),
/// we might want to create a specialization for T/const llvm::Value * that uses
/// the IDs from the IRDB
template <typename T> struct NodeCompressorTraits {
using type = Compressor<T>;

static type create(const ProjectIRDBBase<LLVMProjectIRDB>
* /*IRDB*/) noexcept(noexcept(type())) {
return type();
}
};

template <typename T, typename = void> struct ValCompressorTraits {
using type = Compressor<T>;
using id_type = uint32_t;
};

template <typename T>
struct ValCompressorTraits<T, std::enable_if_t<CanEfficientlyPassByValue<T>>> {
using type = NoneCompressor;
using id_type = T;
};

} // namespace psr

#endif // PHASAR_DATAFLOW_IFDSIDE_SOLVER_COMPRESSOR_H
6 changes: 3 additions & 3 deletions include/phasar/DataFlow/IfdsIde/Solver/ESGEdgeKind.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H
#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H

/******************************************************************************
* Copyright (c) 2022 Philipp Schubert.
* All rights reserved. This program and the accompanying materials are made
Expand All @@ -7,9 +10,6 @@
* Fabian Schiebel and others
*****************************************************************************/

#ifndef PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H
#define PHASAR_DATAFLOW_IFDSIDE_SOLVER_ESGEDGEKIND_H

namespace psr {
enum class ESGEdgeKind { Normal, Call, CallToRet, SkipUnknownFn, Ret, Summary };

Expand Down
Loading

0 comments on commit 64a7233

Please sign in to comment.