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

Pull in the IDESolver++ (aka. IterativeIDESolver) #733

Merged
merged 12 commits into from
Nov 10, 2024
4 changes: 4 additions & 0 deletions include/phasar/ControlFlow/ICFGBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,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
117 changes: 117 additions & 0 deletions include/phasar/DataFlow/IfdsIde/GenericFlowFunction.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/******************************************************************************
* 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_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
188 changes: 188 additions & 0 deletions include/phasar/DataFlow/IfdsIde/Solver/Compressor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#pragma once

#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
Loading
Loading