Skip to content
Draft
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
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,9 @@ jobs:
cd ./examples/how-to
cmake -S . -B build -Dphasar_ROOT=../../INSTALL
cmake --build ./build --target run_sample_programs

- name: Check Test Coverage and generate HTML report
uses: threeal/[email protected]
with:
gcov-executable: llvm-cov gcov
html-out: coverage.html
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
[submodule "external/json-schema-validator"]
path = external/json-schema-validator
url = https://github.com/pboettch/json-schema-validator.git
[submodule "external/cmake-scripts"]
path = external/cmake-scripts
url = https://github.com/StableCoder/cmake-scripts.git
40 changes: 37 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ set(RELEASE_CONFIGURATIONS RELWITHDEBINFO RELEASE CACHE INTERNAL "" FORCE)
# https://reviews.llvm.org/D157613

string(APPEND CMAKE_CXX_FLAGS " -MP -fstack-protector-strong -ffunction-sections -fdata-sections -pipe")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -fno-omit-frame-pointer --coverage -fprofile-arcs -ftest-coverage")
string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -fno-omit-frame-pointer")
string(APPEND CMAKE_CXX_FLAGS_RELEASE "")

Expand Down Expand Up @@ -270,11 +270,45 @@ find_package(Boost 1.65.1 COMPONENTS graph REQUIRED CONFIG)
set(CMAKE_CXX_CLANG_TIDY "")

# Nlohmann JSON

include(add_nlohmann_json)
add_nlohmann_json()
add_json_schema_validator()

# StableCoder cmake-scripts
# TODO: ask fabian if not release or just equal debug is better

# TODO: add install lcov to bootstrap or something
# Do NOT do: sudo apt-get install -y lcov
# Apparently this version is insanely old and we need a newer version/the newest version
# Installing lcov via the github page + make install has four billion dependencies, so
# we need to install those too:
# These perl packages include:
#
# - Capture::Tiny
# - DateTime
# - Devel::Cover
# - Digest::MD5
# - File::Spec
# - at least one flavor of JSON module.
# In order of performance/preference:
# - JSON::XS
# - Cpanel::JSON::XS
# - JSON::PP
# - JSON
# - Memory::Process
# - Module::Load::Conditional
# - Scalar::Util
# - Time::HiRes
# - TimeDate

if (NOT CMAKE_BUILD_TYPE STREQUAL "Release")
set(CODE_COVERAGE ON)
include(external/cmake-scripts/code-coverage.cmake)

set(COV_FILEPATH_FILTER "(usr/local/*|external/)")
add_code_coverage_all_targets(EXCLUDE ${COV_FILEPATH_FILTER} LLVM_EXCLUDE ${COV_FILEPATH_FILTER} LCOV_EXCLUDE ${COV_FILEPATH_FILTER})
endif()

# Googletest
if (NOT PHASAR_IN_TREE)
if(PHASAR_BUILD_UNITTESTS AND NOT TARGET gtest)
Expand All @@ -284,7 +318,7 @@ if (NOT PHASAR_IN_TREE)
FetchContent_Declare(
googletest
GIT_REPOSITORY https://github.com/google/googletest.git
GIT_TAG v1.16.0
GIT_TAG v1.17.0
)
FetchContent_MakeAvailable(googletest)
endif()
Expand Down
2 changes: 2 additions & 0 deletions cmake/phasar_macros.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ function(add_phasar_unittest test_name)
)
set_tests_properties("${test}" PROPERTIES LABELS "all")
set(CTEST_OUTPUT_ON_FAILURE ON)

target_code_coverage(${test} AUTO ALL)
endfunction()

function(validate_binary_version result item)
Expand Down
4 changes: 2 additions & 2 deletions include/phasar/ControlFlow/CallGraphBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ template <typename Derived> class CallGraphBase : public CRTPBase<Derived> {
/// NOTE: This function is typically called in a hot part of the analysis and
/// should therefore be very fast
[[nodiscard]] decltype(auto) getCalleesOfCallAt(ByConstRef<n_t> Inst) const
noexcept(noexcept(self().getCalleesOfCallAtImpl(Inst))) {
noexcept(noexcept(this->self().getCalleesOfCallAtImpl(Inst))) {
static_assert(
is_iterable_over_v<decltype(self().getCalleesOfCallAtImpl(Inst)), f_t>);
return self().getCalleesOfCallAtImpl(Inst);
Expand All @@ -47,7 +47,7 @@ template <typename Derived> class CallGraphBase : public CRTPBase<Derived> {
/// call the given function induced by the used call-graph.
[[nodiscard]] decltype(auto) getCallersOf(ByConstRef<f_t> Fun) const {
static_assert(
is_iterable_over_v<decltype(self().getCallersOfImpl(Fun)), n_t>);
is_iterable_over_v<decltype(this->self().getCallersOfImpl(Fun)), n_t>);
return self().getCallersOfImpl(Fun);
}
};
Expand Down
21 changes: 16 additions & 5 deletions include/phasar/DataFlow/IfdsIde/EdgeFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,21 +442,32 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase {
typename = std::enable_if_t<
!std::is_same_v<EdgeFunction, std::decay_t<ConcreteEF>> &&
IsEdgeFunction<ConcreteEF>>>
[[nodiscard]] friend bool operator==(EdgeFunctionRef<ConcreteEF> LHS,
const EdgeFunction &RHS) noexcept {
if (!RHS.template isa<ConcreteEF>()) {
[[nodiscard]] bool equals(EdgeFunctionRef<ConcreteEF> Other) const noexcept {
// NOTE: Workaround issue in g++ that does not allow transitive friends: If
// putting this code in the operator== below, we cannot access
// Other.Instance, although it is friended...
if (!isa<ConcreteEF>()) {
return false;
}
if (LHS.Instance == RHS.EF) {
if (Other.Instance == EF) {
return true;
}
if constexpr (IsEqualityComparable<ConcreteEF>) {
return *LHS == *getPtr<ConcreteEF>(RHS.EF);
return *Other == *getPtr<ConcreteEF>(EF);
} else {
return true;
}
}

template <typename ConcreteEF,
typename = std::enable_if_t<
!std::is_same_v<EdgeFunction, std::decay_t<ConcreteEF>> &&
IsEdgeFunction<ConcreteEF>>>
[[nodiscard]] friend bool operator==(EdgeFunctionRef<ConcreteEF> LHS,
const EdgeFunction &RHS) noexcept {
return RHS.equals(LHS);
}

template <typename ConcreteEF,
typename = std::enable_if_t<
!std::is_same_v<EdgeFunction, std::decay_t<ConcreteEF>> &&
Expand Down
2 changes: 1 addition & 1 deletion include/phasar/DataFlow/IfdsIde/FlowFunctions.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ template <typename D, typename Container = std::set<D>> class FlowFunction {
template <typename FF> struct IsFlowFunction {
template <typename D, typename Container>
static std::true_type test(const FlowFunction<D, Container> &);
static std::false_type test(...) {}
static std::false_type test(...);

static constexpr bool value = // NOLINT
std::is_same_v<std::true_type,
Expand Down
148 changes: 1 addition & 147 deletions include/phasar/DataFlow/IfdsIde/Solver/Compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,158 +3,12 @@

#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 "phasar/Utils/Compressor.h"

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

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

/// \brief A utility class that assigns a sequential Id to every inserted
/// object.
///
/// This specialization handles types that can be efficiently passed by value
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;
};

/// \brief A utility class that assigns a sequential Id to every inserted
/// object.
///
/// This specialization handles types that cannot be efficiently passed by value
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);
}

/// Returns the index of the given element in the compressors storage. If the
/// element isn't present yet, it will be added first and its index will
/// then be returned.
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;
}

/// Returns the index of the given element in the compressors storage. If the
/// element isn't present yet, it will be added first and its index will
/// then be returned.
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;
}

/// Returns the index of the given element in the compressors storage. If the
/// element isn't present, std::nullopt will be returned
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;
Expand Down
Loading
Loading