From cad8c024a2d094d1a60bec27ab0fac56b3c9e82f Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Mon, 21 Oct 2024 20:41:59 +0200 Subject: [PATCH 1/3] Make the compose and join functions in the EdgeFunction implementations optional, if the IDETabulkationProblem impl provides extend and combine --- .../phasar/DataFlow/IfdsIde/EdgeFunction.h | 74 +++++++++++++------ .../Problems/IDEInstInteractionAnalysis.h | 22 ------ include/phasar/Utils/ErrorFwd.h | 22 ++++++ lib/Utils/ErrorFwd.cpp | 26 +++++++ 4 files changed, 101 insertions(+), 43 deletions(-) create mode 100644 include/phasar/Utils/ErrorFwd.h create mode 100644 lib/Utils/ErrorFwd.cpp diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index ee7ee4b92c..57bc1941ed 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -12,6 +12,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunctionSingletonCache.h" #include "phasar/Utils/ByRef.h" +#include "phasar/Utils/ErrorFwd.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/DenseMapInfo.h" @@ -41,28 +42,48 @@ template struct IsEdgeFunction : std::false_type {}; template struct IsEdgeFunction< - T, std::void_t< - typename T::l_t, - decltype(std::declval().computeTarget( - std::declval())), - decltype(T::compose(std::declval>(), - std::declval>())), - decltype(T::join(std::declval>(), - std::declval>()))>> + T, std::void_t().computeTarget( + std::declval()))>> : std::true_type {}; + +template struct HasEFCompose : std::false_type {}; +template +struct HasEFCompose>(), + std::declval>()))>> : std::true_type {}; +template struct HasEFJoin : std::false_type {}; +template +struct HasEFJoin>(), + std::declval>()))>> + : std::true_type {}; } // namespace detail template static constexpr bool IsEdgeFunction = detail::IsEdgeFunction::value; +template +static constexpr bool HasEFCompose = detail::HasEFCompose::value; +template +static constexpr bool HasEFJoin = detail::HasEFJoin::value; #else // clang-format off template -concept IsEdgeFunction = requires(const T &EF, const EdgeFunction& TEEF, EdgeFunctionRef CEF, typename T::l_t Src) { +concept IsEdgeFunction = requires(const T &EF, typename T::l_t Src) { typename T::l_t; {EF.computeTarget(Src)} -> std::convertible_to; - {T::compose(CEF, TEEF)} -> std::same_as>; - {T::join(CEF, TEEF)} -> std::same_as>; +}; + +template +concept HasEFCompose = requires(EdgeFunctionRef EFRef, + const EdgeFunction &EF) { + { T::compose(EFRef, EF) } -> std::convertible_to>; +}; +template +concept HasEFJoin = requires(EdgeFunctionRef EFRef, + const EdgeFunction &EF) { + { T::join(EFRef, EF) } -> std::convertible_to>; }; // clang-format on @@ -700,17 +721,28 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return getPtr(EF)->computeTarget(Source); }, [](const void *EF, const EdgeFunction &SecondEF, - AllocationPolicy Policy) { - return ConcreteEF::compose( - EdgeFunctionRef( - EF, Policy == AllocationPolicy::CustomHeapAllocated), - SecondEF); + AllocationPolicy Policy) -> EdgeFunction { + if constexpr (HasEFCompose) { + return ConcreteEF::compose( + EdgeFunctionRef( + EF, Policy == AllocationPolicy::CustomHeapAllocated), + SecondEF); + } else { + composeEFPureVirtualError(llvm::getTypeName(), + llvm::getTypeName()); + } }, - [](const void *EF, const EdgeFunction &OtherEF, AllocationPolicy Policy) { - return ConcreteEF::join( - EdgeFunctionRef( - EF, Policy == AllocationPolicy::CustomHeapAllocated), - OtherEF); + [](const void *EF, const EdgeFunction &OtherEF, + AllocationPolicy Policy) -> EdgeFunction { + if constexpr (HasEFJoin) { + return ConcreteEF::join( + EdgeFunctionRef( + EF, Policy == AllocationPolicy::CustomHeapAllocated), + OtherEF); + } else { + joinEFPureVirtualError(llvm::getTypeName(), + llvm::getTypeName()); + } }, [](const void *EF1, const void *EF2) noexcept { static_assert(IsEqualityComparable || diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h index b4410d1f99..0e12d451c1 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEInstInteractionAnalysis.h @@ -950,17 +950,6 @@ class IDEInstInteractionAnalysisT l_t computeTarget(ByConstRef /* Src */) const { return Replacement; } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - bool operator==(const IIAAKillOrReplaceEF &Other) const noexcept { return Replacement == Other.Replacement; } @@ -1001,17 +990,6 @@ class IDEInstInteractionAnalysisT return IDEInstInteractionAnalysisT::joinImpl(Src, Data); } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction & /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - bool operator==(const IIAAAddLabelsEF &Other) const noexcept { return Data == Other.Data; } diff --git a/include/phasar/Utils/ErrorFwd.h b/include/phasar/Utils/ErrorFwd.h new file mode 100644 index 0000000000..d6e93fd043 --- /dev/null +++ b/include/phasar/Utils/ErrorFwd.h @@ -0,0 +1,22 @@ +/****************************************************************************** + * Copyright (c) 2024 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_UTILS_ERRORFWD_H +#define PHASAR_UTILS_ERRORFWD_H + +#include "llvm/ADT/StringRef.h" + +namespace psr { +[[noreturn, gnu::cold]] void +composeEFPureVirtualError(llvm::StringRef ConcreteEF, llvm::StringRef L); +[[noreturn, gnu::cold]] void joinEFPureVirtualError(llvm::StringRef ConcreteEF, + llvm::StringRef L); +} // namespace psr + +#endif // PHASAR_UTILS_ERRORFWD_H diff --git a/lib/Utils/ErrorFwd.cpp b/lib/Utils/ErrorFwd.cpp new file mode 100644 index 0000000000..3f8766becc --- /dev/null +++ b/lib/Utils/ErrorFwd.cpp @@ -0,0 +1,26 @@ +#include "phasar/Utils/ErrorFwd.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" + +void psr::composeEFPureVirtualError(llvm::StringRef ConcreteEF, + llvm::StringRef L) { + llvm::report_fatal_error( + "EdgeFunction composition is not implemented for " + ConcreteEF + + "; Either implement static EdgeFunction<" + L + + "> compose(const EdgeFunctionRef<" + ConcreteEF + + ">, const EdgeFunction<" + L + ">&) in " + ConcreteEF + + ", or override EdgeFunction<" + L + "> extend(const EdgeFunction<" + L + + ">&, const EdgeFunction<" + L + ">&) in your IDETabulationProblem"); +} + +void psr::joinEFPureVirtualError(llvm::StringRef ConcreteEF, + llvm::StringRef L) { + llvm::report_fatal_error( + "EdgeFunction join is not implemented for " + ConcreteEF + + "; Either implement static EdgeFunction<" + L + + "> join(const EdgeFunctionRef<" + ConcreteEF + ">, const EdgeFunction<" + + L + ">&) in " + ConcreteEF + ", or override EdgeFunction<" + L + + "> combine(const EdgeFunction<" + L + ">&, const EdgeFunction<" + L + + ">&) in your IDETabulationProblem"); +} From a7e105f4e3563cbb521dc4d0d15d91e445a99ccc Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Tue, 10 Feb 2026 19:40:51 +0100 Subject: [PATCH 2/3] Remove obsolete compose/join dummies from IDEFeatureTaintAnalysis --- .../Problems/IDEFeatureTaintAnalysis.cpp | 44 ------------------- 1 file changed, 44 deletions(-) diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp index 729a493b67..2ec25c0bfe 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp @@ -315,17 +315,6 @@ struct IDEFeatureTaintAnalysis::AddFactsEF { return Source; } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction & /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - friend bool operator==(const AddFactsEF &L, const AddFactsEF &R) { return L.Facts == R.Facts; } @@ -346,17 +335,6 @@ struct IDEFeatureTaintAnalysis::GenerateEF { return Facts; } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction & /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - friend bool operator==(const GenerateEF &L, const GenerateEF &R) { return L.Facts == R.Facts; } @@ -377,17 +355,6 @@ struct AddSmallFactsEF { return Source; } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction & /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - // NOLINTNEXTLINE -- unused function -- must satisfy the EF interface friend bool operator==(const AddSmallFactsEF &L, const AddSmallFactsEF &R) { return L.Facts == R.Facts; @@ -410,17 +377,6 @@ struct GenerateSmallEF { return Facts; } - static EdgeFunction - compose(EdgeFunctionRef /*This*/, - const EdgeFunction & /*SecondFunction*/) { - llvm::report_fatal_error("Implemented in 'extend'"); - } - - static EdgeFunction join(EdgeFunctionRef /*This*/, - const EdgeFunction & /*OtherFunction*/) { - llvm::report_fatal_error("Implemented in 'combine'"); - } - // NOLINTNEXTLINE -- unused function -- must satisfy the EF interface friend llvm::hash_code hash_value(const GenerateSmallEF &EF) { return llvm::hash_value(EF.Facts); From d92da6a47fa9e863591aa32222b58a12b6d1b541 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Tue, 10 Feb 2026 20:26:00 +0100 Subject: [PATCH 3/3] Use explicit vtable thunks in EdgeFunction to allow function merging + minor --- .../phasar/DataFlow/IfdsIde/EdgeFunction.h | 247 ++++++++++-------- .../DataFlow/IfdsIde/EdgeFunctionUtils.h | 39 +-- include/phasar/Domain/LatticeDomain.h | 55 ++-- include/phasar/Utils/JoinLattice.h | 3 +- 4 files changed, 180 insertions(+), 164 deletions(-) diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 30870feea2..5b937aa410 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -38,12 +38,10 @@ template class EdgeFunction; template class EdgeFunctionRef; template -concept IsEdgeFunction = - requires(const T &EF, const EdgeFunction &TEEF, - EdgeFunctionRef CEF, typename T::l_t Src) { - typename T::l_t; - { EF.computeTarget(Src) } -> std::convertible_to; - }; +concept IsEdgeFunction = requires(const T &EF, typename T::l_t Src) { + typename T::l_t; + { EF.computeTarget(Src) } -> std::convertible_to; +}; template concept HasEFCompose = requires(EdgeFunctionRef EFRef, @@ -112,21 +110,23 @@ class EdgeFunctionBase { /// \brief Non-null reference to an edge function that is guarenteed to be /// managed by an EdgeFunction object. template -class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase { +class [[gsl::Pointer(EF)]] EdgeFunctionRef final : EdgeFunctionBase { template friend class EdgeFunction; public: using l_t = typename EF::l_t; - EdgeFunctionRef(const EdgeFunctionRef &) noexcept = default; - EdgeFunctionRef &operator=(const EdgeFunctionRef &) noexcept = default; - ~EdgeFunctionRef() = default; - - const EF *operator->() const noexcept { return getPtr(Instance); } - const EF *get() const noexcept { return getPtr(Instance); } - const EF &operator*() const noexcept { return *getPtr(Instance); } + [[nodiscard]] constexpr const EF *operator->() const noexcept { + return getPtr(Instance); + } + [[nodiscard]] constexpr const EF *get() const noexcept { + return getPtr(Instance); + } + [[nodiscard]] constexpr const EF &operator*() const noexcept { + return *getPtr(Instance); + } - [[nodiscard]] bool isCached() const noexcept { + [[nodiscard]] constexpr bool isCached() const noexcept { if constexpr (IsSOOCandidate) { return false; } else { @@ -134,7 +134,7 @@ class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase { } } - [[nodiscard]] EdgeFunctionSingletonCache * + [[nodiscard]] constexpr EdgeFunctionSingletonCache * getCacheOrNull() const noexcept { if (isCached()) { return static_cast *>(Instance)->Cache; @@ -160,46 +160,46 @@ class [[clang::trivial_abi]] EdgeFunctionRef final : EdgeFunctionBase { template // -- combined copy and move assignment // NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) -class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { +class [[clang::trivial_abi, gsl::Owner]] EdgeFunction final : EdgeFunctionBase { public: using l_t = L; // --- Constructors /// Default-initializes the edge-function with nullptr - EdgeFunction() noexcept = default; + constexpr EdgeFunction() noexcept = default; /// Default-initializes the edge-function with nullptr - EdgeFunction(std::nullptr_t) noexcept : EdgeFunction() {} + constexpr EdgeFunction(std::nullptr_t) noexcept : EdgeFunction() {} /// Copy constructor. Increments the ref-count, if not small-object-optimized. EdgeFunction(const EdgeFunction &Other) noexcept : EdgeFunction(Other.EF, Other.VTAndHeapAlloc) {} /// Move constructor. Does not increment the ref-count, but instead leaves the /// moved-from edge function in the nullptr state. - EdgeFunction(EdgeFunction &&Other) noexcept + constexpr EdgeFunction(EdgeFunction &&Other) noexcept : EF(std::exchange(Other.EF, nullptr)), VTAndHeapAlloc( std::exchange(Other.VTAndHeapAlloc, decltype(VTAndHeapAlloc){})) {} /// Standard swap; does not affect ref-counts - void swap(EdgeFunction &Other) noexcept { + constexpr void swap(EdgeFunction &Other) noexcept { std::swap(EF, Other.EF); std::swap(VTAndHeapAlloc, Other.VTAndHeapAlloc); } /// Standard swap; does not affect ref-counts - friend void swap(EdgeFunction &LHS, EdgeFunction &RHS) noexcept { + constexpr friend void swap(EdgeFunction &LHS, EdgeFunction &RHS) noexcept { LHS.swap(RHS); } /// Combined copy- and move assignment. If the assigned-to edge function is /// not null, invokes the destructor on it, before overwriting its content. - EdgeFunction &operator=(EdgeFunction Other) noexcept { + constexpr EdgeFunction &operator=(EdgeFunction Other) noexcept { std::destroy_at(this); return *new (this) EdgeFunction(std::move(Other)); // NOLINT } /// Null-assignment operator. Decrements the ref-count if not /// small-object-optimized or already null. Leaves the assigned-to edge /// function in the nullptr state. - EdgeFunction &operator=(std::nullptr_t) noexcept { + constexpr EdgeFunction &operator=(std::nullptr_t) noexcept { std::destroy_at(this); return *new (this) EdgeFunction(); // NOLINT } @@ -582,7 +582,9 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// Gets an opaque identifier for this edge function. Only meant for /// comparisons of object-identity. Do not dereference! - [[nodiscard]] const void *getOpaqueValue() const noexcept { return EF; } + [[nodiscard]] constexpr const void *getOpaqueValue() const noexcept { + return EF; + } /// Gets the cache where this edge function is being cached in. If this edge /// function is not cached (i.e., isCached() returns false), returns nullptr. @@ -647,94 +649,117 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { // NOLINTEND(readability-identifier-naming) }; + template + static l_t computeTargetThunk(const void *EF, ByConstRef Source) { + return getPtr(EF)->computeTarget(Source); + } + + template + static EdgeFunction composeThunk(const void *EF, const EdgeFunction &SecondEF, + AllocationPolicy Policy) { + if constexpr (HasEFCompose) { + return ConcreteEF::compose( + EdgeFunctionRef( + EF, Policy == AllocationPolicy::CustomHeapAllocated), + SecondEF); + } else { + composeEFPureVirtualError(llvm::getTypeName(), + llvm::getTypeName()); + } + } + + template + static EdgeFunction joinThunk(const void *EF, const EdgeFunction &OtherEF, + AllocationPolicy Policy) { + if constexpr (HasEFJoin) { + return ConcreteEF::join( + EdgeFunctionRef( + EF, Policy == AllocationPolicy::CustomHeapAllocated), + OtherEF); + } else { + joinEFPureVirtualError(llvm::getTypeName(), + llvm::getTypeName()); + } + } + + template + static bool equalsThunk(const void *EF1, const void *EF2) noexcept { + static_assert(IsEqualityComparable || + std::is_empty_v, + "An EdgeFunction must be equality comparable with " + "operator==. Only if the type is empty, i.e. has no " + "members, the comparison can be inferred."); + if constexpr (IsEqualityComparable) { + return *getPtr(EF1) == *getPtr(EF2); + } else { + return true; + } + } + + template + static void printThunk(const void *EF, llvm::raw_ostream &OS) { + if constexpr (is_llvm_printable_v) { + OS << *getPtr(EF); + } else { + OS << llvm::getTypeName(); + } + } + + template + static bool isConstantThunk(const void *EF) noexcept { + if constexpr (HasIsConstant) { + static_assert( + std::is_nothrow_invocable_v, + "The function isConstant() must be noexcept!"); + return getPtr(EF)->isConstant(); + } else { + return false; + } + } + + template + static void destroyThunk(const void *EF, AllocationPolicy Policy) noexcept { + if constexpr (!IsSOOCandidate) { + if (Policy != AllocationPolicy::CustomHeapAllocated) { + assert(Policy == AllocationPolicy::DefaultHeapAllocated); + delete static_cast *>(EF); + } else { + auto CEF = static_cast *>(EF); + CEF->Cache->erase(CEF->Value); + delete CEF; + } + } + } + + template + static size_t getHashCodeThunk(const void *EF, const void *VT) noexcept { + if constexpr (is_std_hashable_v) { + return std::hash{}(*getPtr(EF)); + } else if constexpr (is_llvm_hashable_v) { + using llvm::hash_value; + return hash_value(*getPtr(EF)); + } else { + return llvm::hash_combine(EF, VT); + } + } + + template + static size_t depthThunk(const void *EF) noexcept { + if constexpr (HasDepth) { + return getPtr(EF)->depth(); + } else { + return 1; + } + } + template static constexpr VTable VTableFor = { - [](const void *EF, ByConstRef Source) -> l_t { - return getPtr(EF)->computeTarget(Source); - }, - [](const void *EF, const EdgeFunction &SecondEF, - AllocationPolicy Policy) -> EdgeFunction { - if constexpr (HasEFCompose) { - return ConcreteEF::compose( - EdgeFunctionRef( - EF, Policy == AllocationPolicy::CustomHeapAllocated), - SecondEF); - } else { - composeEFPureVirtualError(llvm::getTypeName(), - llvm::getTypeName()); - } - }, - [](const void *EF, const EdgeFunction &OtherEF, - AllocationPolicy Policy) -> EdgeFunction { - if constexpr (HasEFJoin) { - return ConcreteEF::join( - EdgeFunctionRef( - EF, Policy == AllocationPolicy::CustomHeapAllocated), - OtherEF); - } else { - joinEFPureVirtualError(llvm::getTypeName(), - llvm::getTypeName()); - } - }, - [](const void *EF1, const void *EF2) noexcept -> bool { - static_assert(IsEqualityComparable || - std::is_empty_v, - "An EdgeFunction must be equality comparable with " - "operator==. Only if the type is empty, i.e. has no " - "members, the comparison can be inferred."); - if constexpr (IsEqualityComparable) { - return *getPtr(EF1) == *getPtr(EF2); - } else { - return true; - } - }, - [](const void *EF, llvm::raw_ostream &OS) { - if constexpr (is_llvm_printable_v) { - OS << *getPtr(EF); - } else { - OS << llvm::getTypeName(); - } - }, - [](const void *EF) noexcept -> bool { - if constexpr (HasIsConstant) { - static_assert( - std::is_nothrow_invocable_v, - "The function isConstant() must be noexcept!"); - return getPtr(EF)->isConstant(); - } else { - return false; - } - }, - [](const void *EF, AllocationPolicy Policy) noexcept { - if constexpr (!IsSOOCandidate) { - if (Policy != AllocationPolicy::CustomHeapAllocated) { - assert(Policy == AllocationPolicy::DefaultHeapAllocated); - delete static_cast *>(EF); - } else { - auto CEF = static_cast *>(EF); - CEF->Cache->erase(CEF->Value); - delete CEF; - } - } - }, - [](const void *EF, const void *VT) noexcept -> size_t { - if constexpr (is_std_hashable_v) { - return std::hash{}(*getPtr(EF)); - } else if constexpr (is_llvm_hashable_v) { - using llvm::hash_value; - return hash_value(*getPtr(EF)); - } else { - return llvm::hash_combine(EF, VT); - } - }, - [](const void *EF) noexcept -> size_t { - if constexpr (HasDepth) { - return getPtr(EF)->depth(); - } else { - return 1; - } - }, + &computeTargetThunk, &composeThunk, + &joinThunk, &equalsThunk, + &printThunk, &isConstantThunk, + &destroyThunk, &getHashCodeThunk, + &depthThunk, }; // Utility ctor for (copy) construction. Increments the ref-count if diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h index 4a28b5694c..5169ce258e 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h @@ -24,14 +24,15 @@ namespace psr { template struct EdgeIdentity final { using l_t = L; - [[nodiscard]] ByConstRef computeTarget(ByConstRef Source) const + [[nodiscard]] constexpr ByConstRef + computeTarget(ByConstRef Source) const noexcept(std::is_nothrow_move_constructible_v) { static_assert(std::is_trivially_copyable_v); static_assert(IsEdgeFunction); return Source; } - [[nodiscard]] static EdgeFunction + [[nodiscard]] constexpr static EdgeFunction compose(EdgeFunctionRef /*This*/, const EdgeFunction &SecondFunction) { return SecondFunction; @@ -46,7 +47,7 @@ template struct ConstantEdgeFunction { using JLattice = JoinLatticeTraits; using value_type = typename NonTopBotValue::type; - [[nodiscard]] l_t computeTarget(ByConstRef /*Source*/) const + [[nodiscard]] constexpr l_t computeTarget(ByConstRef /*Source*/) const noexcept(std::is_nothrow_constructible_v) { static_assert(IsEdgeFunction); return Value; @@ -103,7 +104,8 @@ template struct AllBottom final { [[no_unique_address]] std::conditional_t, EmptyType, l_t> BottomValue; - [[nodiscard]] l_t computeTarget(ByConstRef /*Source*/) const noexcept { + [[nodiscard]] constexpr l_t + computeTarget(ByConstRef /*Source*/) const noexcept { static_assert(std::is_trivially_copyable_v); static_assert(IsEdgeFunction); if constexpr (HasJoinLatticeTraits) { @@ -113,7 +115,7 @@ template struct AllBottom final { } } - [[nodiscard]] static EdgeFunction + [[nodiscard]] constexpr static EdgeFunction compose(EdgeFunctionRef This, const EdgeFunction &SecondFunction) { if (SecondFunction.isConstant()) { @@ -137,7 +139,7 @@ template struct AllBottom final { // return SecondFunction.isConstant() ? SecondFunction : This; } - [[nodiscard]] static EdgeFunction + [[nodiscard]] constexpr static EdgeFunction join(EdgeFunctionRef This, const EdgeFunction & /*OtherFunction*/) { return This; @@ -160,7 +162,8 @@ template struct AllTop final { [[no_unique_address]] std::conditional_t, EmptyType, l_t> TopValue; - [[nodiscard]] l_t computeTarget(ByConstRef /*Source*/) const noexcept { + [[nodiscard]] constexpr l_t + computeTarget(ByConstRef /*Source*/) const noexcept { static_assert(std::is_trivially_copyable_v); static_assert(IsEdgeFunction); if constexpr (HasJoinLatticeTraits) { @@ -176,7 +179,7 @@ template struct AllTop final { return llvm::isa>(SecondFunction) ? This : SecondFunction; } - [[nodiscard]] static EdgeFunction + [[nodiscard]] constexpr static EdgeFunction join(EdgeFunctionRef /*This*/, const EdgeFunction &OtherFunction) { return OtherFunction; @@ -184,7 +187,8 @@ template struct AllTop final { [[nodiscard]] constexpr bool isConstant() const noexcept { return true; } - friend bool operator==(const AllTop &LHS, const AllTop &RHS) noexcept + constexpr friend bool operator==(const AllTop &LHS, + const AllTop &RHS) noexcept requires(!HasJoinLatticeTraits) { return LHS.TopValue == RHS.TopValue; @@ -192,7 +196,7 @@ template struct AllTop final { }; template -EdgeFunction +inline EdgeFunction defaultComposeOrNull(EdgeFunctionRef This, const EdgeFunction &SecondFunction) noexcept { if (llvm::isa>(SecondFunction)) { @@ -205,7 +209,7 @@ defaultComposeOrNull(EdgeFunctionRef This, } template -EdgeFunction +inline EdgeFunction defaultComposeOrNull(const EdgeFunction &This, const EdgeFunction &SecondFunction) noexcept { if (llvm::isa>(SecondFunction)) { @@ -388,8 +392,8 @@ template struct JoinEdgeFunction { /// Joining with EdgeIdentity will overapproximate to (AllBottom if N==0, else /// JoinEdgeFunction). template -EdgeFunction defaultJoinOrNull(EdgeFunctionRef This, - const EdgeFunction &OtherFunction) { +inline EdgeFunction defaultJoinOrNull(EdgeFunctionRef This, + const EdgeFunction &OtherFunction) { if (llvm::isa>(OtherFunction)) { return OtherFunction; } @@ -407,8 +411,8 @@ EdgeFunction defaultJoinOrNull(EdgeFunctionRef This, } template -EdgeFunction defaultJoinOrNull(const EdgeFunction &This, - const EdgeFunction &OtherFunction) { +inline EdgeFunction defaultJoinOrNull(const EdgeFunction &This, + const EdgeFunction &OtherFunction) { if (llvm::isa>(OtherFunction) || llvm::isa>(This)) { return OtherFunction; } @@ -427,8 +431,9 @@ EdgeFunction defaultJoinOrNull(const EdgeFunction &This, } template -EdgeFunction EdgeIdentity::join(EdgeFunctionRef This, - const EdgeFunction &OtherFunction) { +inline EdgeFunction +EdgeIdentity::join(EdgeFunctionRef This, + const EdgeFunction &OtherFunction) { if (llvm::isa>(OtherFunction) || llvm::isa>(OtherFunction)) { return This; diff --git a/include/phasar/Domain/LatticeDomain.h b/include/phasar/Domain/LatticeDomain.h index 27092a02f8..67a1b32218 100644 --- a/include/phasar/Domain/LatticeDomain.h +++ b/include/phasar/Domain/LatticeDomain.h @@ -65,17 +65,17 @@ template struct LatticeDomain : public std::variant { using std::variant::variant; - [[nodiscard]] inline bool isBottom() const noexcept { + [[nodiscard]] constexpr bool isBottom() const noexcept { return std::holds_alternative(*this); } - [[nodiscard]] inline bool isTop() const noexcept { + [[nodiscard]] constexpr bool isTop() const noexcept { return std::holds_alternative(*this); } - [[nodiscard]] inline L *getValueOrNull() noexcept { + [[nodiscard]] constexpr L *getValueOrNull() noexcept { return std::get_if(this); } - [[nodiscard]] inline const L *getValueOrNull() const noexcept { + [[nodiscard]] constexpr const L *getValueOrNull() const noexcept { return std::get_if(this); } @@ -91,11 +91,11 @@ struct LatticeDomain : public std::variant { return hash_value(std::get(LD)); } - [[nodiscard]] inline L &assertGetValue() noexcept { + [[nodiscard]] constexpr L &assertGetValue() noexcept { assert(std::holds_alternative(*this)); return std::get(*this); } - [[nodiscard]] inline const L &assertGetValue() const noexcept { + [[nodiscard]] constexpr const L &assertGetValue() const noexcept { assert(std::holds_alternative(*this)); return std::get(*this); } @@ -128,8 +128,8 @@ inline std::ostream &operator<<(std::ostream &OS, const LatticeDomain &LD) { } template -inline bool operator==(const LatticeDomain &Lhs, - const LatticeDomain &Rhs) { +constexpr bool operator==(const LatticeDomain &Lhs, + const LatticeDomain &Rhs) { if (Lhs.index() != Rhs.index()) { return false; } @@ -142,42 +142,27 @@ inline bool operator==(const LatticeDomain &Lhs, template requires AreEqualityComparable -inline bool operator==(const LL &Lhs, const LatticeDomain &Rhs) { - if (auto RVal = Rhs.getValueOrNull()) { - return Lhs == *RVal; +constexpr bool operator==(const LatticeDomain &Lhs, const LL &Rhs) { + if (auto LVal = Lhs.getValueOrNull()) { + return *LVal == Rhs; } return false; } -template - requires AreEqualityComparable -inline bool operator==(const LatticeDomain &Lhs, const LL &Rhs) { - return Rhs == Lhs; -} - template -inline bool operator==(const LatticeDomain &Lhs, Bottom /*Rhs*/) noexcept { +constexpr bool operator==(const LatticeDomain &Lhs, + Bottom /*Rhs*/) noexcept { return Lhs.isBottom(); } template -inline bool operator==(const LatticeDomain &Lhs, Top /*Rhs*/) noexcept { +constexpr bool operator==(const LatticeDomain &Lhs, Top /*Rhs*/) noexcept { return Lhs.isTop(); } template -inline bool operator==(Bottom /*Lhs*/, const LatticeDomain &Rhs) noexcept { - return Rhs.isBottom(); -} - -template -inline bool operator==(Top /*Lhs*/, const LatticeDomain &Rhs) noexcept { - return Rhs.isTop(); -} - -template -inline bool operator<(const LatticeDomain &Lhs, - const LatticeDomain &Rhs) { +constexpr bool operator<(const LatticeDomain &Lhs, + const LatticeDomain &Rhs) { /// Top < (Lhs::L < Rhs::L) < Bottom if (Rhs.isTop()) { return false; @@ -202,8 +187,8 @@ template struct JoinLatticeTraits> { using l_t = L; static constexpr Bottom bottom() noexcept { return {}; } static constexpr Top top() noexcept { return {}; } - static LatticeDomain join(ByConstRef> LHS, - ByConstRef> RHS) { + static constexpr LatticeDomain join(ByConstRef> LHS, + ByConstRef> RHS) { // Top < (Lhs::l_t < Rhs::l_t) < Bottom if (LHS.isTop() || LHS == RHS) { return RHS; @@ -225,7 +210,7 @@ template struct NonTopBotValue> { using type = L; - static L unwrap(LatticeDomain Value) noexcept( + constexpr static L unwrap(LatticeDomain Value) noexcept( std::is_nothrow_move_constructible_v) { return std::get(std::move(Value)); } @@ -235,7 +220,7 @@ struct NonTopBotValue> { namespace std { template struct hash> { - size_t operator()(const psr::LatticeDomain &LD) noexcept { + constexpr size_t operator()(const psr::LatticeDomain &LD) noexcept { if (LD.isBottom()) { return SIZE_MAX; } diff --git a/include/phasar/Utils/JoinLattice.h b/include/phasar/Utils/JoinLattice.h index 27d5069dea..7b4b6a1870 100644 --- a/include/phasar/Utils/JoinLattice.h +++ b/include/phasar/Utils/JoinLattice.h @@ -65,7 +65,8 @@ class JoinLattice { template struct NonTopBotValue { using type = L; - static L unwrap(L Value) noexcept(std::is_nothrow_move_constructible_v) { + constexpr static L + unwrap(L Value) noexcept(std::is_nothrow_move_constructible_v) { return Value; } };