diff --git a/BreakingChanges.md b/BreakingChanges.md index c149ab7c86..ed1f0a9ff2 100644 --- a/BreakingChanges.md +++ b/BreakingChanges.md @@ -2,9 +2,10 @@ ## development HEAD +- `FlowFunctionPtrType` is now a move-only type. - Removed the CRTP-template `ProjectIRDBBase`. Use the concept `ProjectIRDB` instead. -- Remove `EdgeFunction::equals`. Use `operator==` instead. -- Remove ctor-overloads of `LLVMProjectIRDB` that use the `EnableOpaquePointers` parameter. LLVM is removing support for opaque pointers. Use the other ctors instead. +- Removed `EdgeFunction::equals`. Use `operator==` instead. +- Removed ctor-overloads of `LLVMProjectIRDB` that use the `EnableOpaquePointers` parameter. LLVM is removing support for opaque pointers. Use the other ctors instead. - Removed type-trait `has_getAsJson`, as it is not used in our codebase anymore. We switched from `getAsJson` to `printAsJson` a while ago. ## v2604 diff --git a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h index 6c7cc58a87..ec18592c24 100644 --- a/include/phasar/DataFlow/IfdsIde/FlowFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/FlowFunctions.h @@ -18,6 +18,7 @@ #define PHASAR_DATAFLOW_IFDSIDE_FLOWFUNCTIONS_H #include "phasar/Utils/Macros.h" +#include "phasar/Utils/MaybeUniquePtr.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" @@ -45,7 +46,7 @@ template > class FlowFunction { public: using FlowFunctionType = FlowFunction; - using FlowFunctionPtrType = std::shared_ptr; + using FlowFunctionPtrType = MaybeUniquePtr; using container_type = Container; using value_type = typename container_type::value_type; @@ -87,7 +88,7 @@ concept is_flowfunction_v = IsFlowFunction::value; // NOLINT /// Given a flow-function type FF, returns a (smart) pointer type pointing to FF template -using FlowFunctionPtrTypeOf = std::shared_ptr; +using FlowFunctionPtrTypeOf = MaybeUniquePtr; /// Given a dataflow-fact type and optionally a container-type, returns a /// (smart) pointer type pointing to a FlowFunction with the specified @@ -147,6 +148,8 @@ template class FlowFunctionTemplates { using d_t = D; using container_type = Container; + using FlowFunctionPtrType = psr::FlowFunctionPtrType; + /// A flow function that propagates all incoming facts unchanged. /// /// Given a flow-function f = identityFlow(), then for all incoming @@ -160,15 +163,18 @@ template class FlowFunctionTemplates { /// v v v ... /// x1 x2 x3 ... /// \endcode - static auto identityFlow() { + /// \note Returns a non-owning pointer to a function-static singleton + /// (owns()==false). The pointee has program lifetime. Most other factory + /// functions in this class return owning unique_ptrs. + static FlowFunctionPtrType identityFlow() { struct IdFF final : public FlowFunction { container_type computeTargets(d_t Source) override { return {std::move(Source)}; } }; - static auto TheIdentity = std::make_shared(); + static auto TheIdentity = IdFF(); - return TheIdentity; + return &TheIdentity; } /// The most generic flow function. Invokes the passed function object F to @@ -186,7 +192,7 @@ template class FlowFunctionTemplates { /// v v v v v /// x1 x2 x x3 x4 /// \endcode - template static auto lambdaFlow(Fn &&F) { + template static FlowFunctionPtrType lambdaFlow(Fn &&F) { struct LambdaFlow final : public FlowFunction { LambdaFlow(Fn &&F) : Flow(std::forward(F)) {} container_type computeTargets(d_t Source) override { @@ -196,7 +202,7 @@ template class FlowFunctionTemplates { [[no_unique_address]] std::decay_t Flow; }; - return std::make_shared(std::forward(F)); + return std::make_unique(std::forward(F)); } //===----------------------------------------------------------------------===// @@ -225,7 +231,7 @@ template class FlowFunctionTemplates { /// no difference, but in the case of IDE, the corresponding edge functions /// are being joined together potentially lowering precision. If that is an /// issue, use transferFlow instead. - static auto generateFlow(d_t FactToGenerate, d_t From) { + static FlowFunctionPtrType generateFlow(d_t FactToGenerate, d_t From) { struct GenFrom final : public FlowFunction { GenFrom(d_t GenValue, d_t FromValue) : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} @@ -241,7 +247,7 @@ template class FlowFunctionTemplates { d_t FromValue; }; - return std::make_shared(std::move(FactToGenerate), + return std::make_unique(std::move(FactToGenerate), std::move(From)); } @@ -257,7 +263,7 @@ template class FlowFunctionTemplates { /// \endcode template requires std::is_invocable_r_v - static auto generateFlowIf(d_t FactToGenerate, Fn Predicate) { + static FlowFunctionPtrType generateFlowIf(d_t FactToGenerate, Fn Predicate) { struct GenFlowIf final : public FlowFunction { GenFlowIf(d_t GenValue, Fn &&Predicate) : GenValue(std::move(GenValue)), @@ -274,7 +280,7 @@ template class FlowFunctionTemplates { [[no_unique_address]] std::decay_t Predicate; }; - return std::make_shared(std::move(FactToGenerate), + return std::make_unique(std::move(FactToGenerate), std::forward(Predicate)); } @@ -296,7 +302,8 @@ template class FlowFunctionTemplates { /// x w v1 v2 ... vN u /// \endcode template Range = std::initializer_list> - static auto generateManyFlows(Range &&FactsToGenerate, d_t From) { + static FlowFunctionPtrType generateManyFlows(Range &&FactsToGenerate, + d_t From) { struct GenMany final : public FlowFunction { GenMany(container_type &&GenValues, d_t FromValue) : GenValues(std::move(GenValues)), FromValue(std::move(FromValue)) {} @@ -314,7 +321,7 @@ template class FlowFunctionTemplates { d_t FromValue; }; - return std::make_shared(detail::makeContainer( + return std::make_unique(detail::makeContainer( std::forward(FactsToGenerate)), std::move(From)); } @@ -338,7 +345,7 @@ template class FlowFunctionTemplates { /// v v /// u v w ... /// \endcode - static auto killFlow(d_t FactToKill) { + static FlowFunctionPtrType killFlow(d_t FactToKill) { struct KillFlow final : public FlowFunction { KillFlow(d_t KillValue) : KillValue(std::move(KillValue)) {} container_type computeTargets(d_t Source) override { @@ -350,7 +357,7 @@ template class FlowFunctionTemplates { d_t KillValue; }; - return std::make_shared(std::move(FactToKill)); + return std::make_unique(std::move(FactToKill)); } /// A flow function similar to killFlow that stops propagating all dataflow @@ -364,7 +371,7 @@ template class FlowFunctionTemplates { /// \endcode template requires std::is_invocable_r_v - static auto killFlowIf(Fn Predicate) { + static FlowFunctionPtrType killFlowIf(Fn Predicate) { struct KillFlowIf final : public FlowFunction { KillFlowIf(Fn &&Predicate) : Predicate(std::forward(Predicate)) {} @@ -378,7 +385,7 @@ template class FlowFunctionTemplates { [[no_unique_address]] std::decay_t Predicate; }; - return std::make_shared(std::forward(Predicate)); + return std::make_unique(std::forward(Predicate)); } /// A flow function that stops propagating a specific set of dataflow facts @@ -402,7 +409,7 @@ template class FlowFunctionTemplates { /// u v1 v2 ... vN w ... /// \endcode template Range = std::initializer_list> - static auto killManyFlows(Range &&FactsToKill) { + static FlowFunctionPtrType killManyFlows(Range &&FactsToKill) { struct KillMany final : public FlowFunction { KillMany(Container &&KillValues) : KillValues(std::move(KillValues)) {} @@ -416,7 +423,7 @@ template class FlowFunctionTemplates { container_type KillValues; }; - return std::make_shared(detail::makeContainer( + return std::make_unique(detail::makeContainer( std::forward(FactsToKill))); } @@ -426,13 +433,16 @@ template class FlowFunctionTemplates { /// \code /// x, f(x) = {}. /// \endcode - static auto killAllFlows() { + /// \note Returns a non-owning pointer to a function-static singleton + /// (owns()==false). The pointee has program lifetime. Most other factory + /// functions in this class return owning unique_ptrs. + static FlowFunctionPtrType killAllFlows() { struct KillAllFF final : public FlowFunction { Container computeTargets(d_t /*Source*/) override { return Container(); } }; - static auto TheKillAllFlow = std::make_shared(); + static auto TheKillAllFlow = KillAllFF(); - return TheKillAllFlow; + return &TheKillAllFlow; } //===----------------------------------------------------------------------===// @@ -460,7 +470,8 @@ template class FlowFunctionTemplates { /// v v /// x w v u /// \endcode - static auto generateFlowAndKillAllOthers(d_t FactToGenerate, d_t From) { + static FlowFunctionPtrType generateFlowAndKillAllOthers(d_t FactToGenerate, + d_t From) { struct GenFlowAndKillAllOthers final : public FlowFunction { GenFlowAndKillAllOthers(d_t GenValue, d_t FromValue) @@ -477,7 +488,7 @@ template class FlowFunctionTemplates { d_t FromValue; }; - return std::make_shared(std::move(FactToGenerate), + return std::make_unique(std::move(FactToGenerate), std::move(From)); } @@ -500,8 +511,8 @@ template class FlowFunctionTemplates { /// x w v1 v2 ... vN u /// \endcode template Range = std::initializer_list> - static auto generateManyFlowsAndKillAllOthers(Range &&FactsToGenerate, - d_t From) { + static FlowFunctionPtrType + generateManyFlowsAndKillAllOthers(Range &&FactsToGenerate, d_t From) { struct GenManyAndKillAllOthers final : public FlowFunction { GenManyAndKillAllOthers(Container &&GenValues, d_t FromValue) @@ -520,7 +531,7 @@ template class FlowFunctionTemplates { d_t FromValue; }; - return std::make_shared( + return std::make_unique( detail::makeContainer( std::forward(FactsToGenerate)), std::move(From)); @@ -553,7 +564,7 @@ template class FlowFunctionTemplates { /// v v v v ... /// x w v u /// \endcode - static auto transferFlow(d_t FactToGenerate, d_t From) { + static FlowFunctionPtrType transferFlow(d_t FactToGenerate, d_t From) { struct TransferFlow final : public FlowFunction { TransferFlow(d_t GenValue, d_t FromValue) : GenValue(std::move(GenValue)), FromValue(std::move(FromValue)) {} @@ -572,7 +583,7 @@ template class FlowFunctionTemplates { d_t FromValue; }; - return std::make_shared(std::move(FactToGenerate), + return std::make_unique(std::move(FactToGenerate), std::move(From)); } @@ -590,8 +601,8 @@ template class FlowFunctionTemplates { std::is_same_v && std::is_same_v) - auto unionFlows(FlowFunctionPtrTypeOf OneFF, - FlowFunctionPtrTypeOf OtherFF) { + FlowFunctionPtrType unionFlows(FlowFunctionPtrTypeOf OneFF, + FlowFunctionPtrTypeOf OtherFF) { struct UnionFlow final : public FlowFunction { UnionFlow(FlowFunctionPtrTypeOf OneFF, FlowFunctionPtrTypeOf OtherFF) noexcept @@ -613,7 +624,7 @@ template class FlowFunctionTemplates { FlowFunctionPtrTypeOf OtherFF; }; - return std::make_shared(std::move(OneFF), std::move(OtherFF)); + return std::make_unique(std::move(OneFF), std::move(OtherFF)); } }; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index 09b48c89c5..7521fb865c 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -11,10 +11,13 @@ #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_FLOWEDGEFUNCTIONCACHE_H #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" +#include "phasar/DataFlow/IfdsIde/FlowFunctions.h" #include "phasar/DataFlow/IfdsIde/IDETabulationProblem.h" #include "phasar/Utils/EquivalenceClassMap.h" #include "phasar/Utils/Logger.h" +#include "phasar/Utils/NonNullPtr.h" #include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/PointerUtils.h" #include "phasar/Utils/Utilities.h" #include "llvm/ADT/DenseMap.h" @@ -41,7 +44,7 @@ template class DefaultMapKeyCompressor { using KeyType = KeyT; using CompressedType = KeyT; - [[nodiscard]] inline CompressedType getCompressedID(KeyT Key) { return Key; } + [[nodiscard]] CompressedType getCompressedID(KeyT Key) { return Key; } }; template class MapKeyCompressorCombinator : public Ts... { @@ -54,7 +57,7 @@ class LLVMMapKeyCompressor { using KeyType = const llvm::Value *; using CompressedType = uint32_t; - [[nodiscard]] inline CompressedType getCompressedID(KeyType Key) { + [[nodiscard]] CompressedType getCompressedID(KeyType Key) { auto Search = Map.find(Key); if (Search == Map.end()) { return Map.insert(std::make_pair(Key, Map.size() + 1)).first->getSecond(); @@ -84,61 +87,8 @@ class FlowEdgeFunctionCache { using t_t = typename AnalysisDomainTy::t_t; using l_t = typename AnalysisDomainTy::l_t; - using DTKeyCompressorType = std::conditional_t< - std::is_base_of_v>, - LLVMMapKeyCompressor, DefaultMapKeyCompressor>; - using NTKeyCompressorType = std::conditional_t< - std::is_base_of_v>, - LLVMMapKeyCompressor, DefaultMapKeyCompressor>; - - using MapKeyCompressorType = std::conditional_t< - std::is_same_v, - NTKeyCompressorType, - MapKeyCompressorCombinator>; - -private: - MapKeyCompressorType KeyCompressor; - - using EdgeFuncInstKey = uint64_t; - using EdgeFuncNodeKey = std::conditional_t< - std::is_base_of_v>, uint64_t, - std::pair>; - using InnerEdgeFunctionMapType = - EquivalenceClassMap>; - - IDETabulationProblem &Problem; - // Auto add zero - bool AutoAddZero; - d_t ZV; - - struct NormalEdgeFlowData { - NormalEdgeFlowData(FlowFunctionPtrType Val) - : FlowFuncPtr(std::move(Val)), EdgeFunctionMap{} {} - NormalEdgeFlowData(InnerEdgeFunctionMapType Map) - : FlowFuncPtr(nullptr), EdgeFunctionMap{std::move(Map)} {} - - FlowFunctionPtrType FlowFuncPtr; - InnerEdgeFunctionMapType EdgeFunctionMap; - }; - - // Caches for the flow/edge functions - std::map NormalFunctionCache; - - // Caches for the flow functions - std::map, FlowFunctionPtrType> CallFlowFunctionCache; - std::map, FlowFunctionPtrType> - ReturnFlowFunctionCache; - std::map, FlowFunctionPtrType> - CallToRetFlowFunctionCache; - // Caches for the edge functions - std::map, EdgeFunction> - CallEdgeFunctionCache; - std::map, EdgeFunction> - ReturnEdgeFunctionCache; - std::map - CallToRetEdgeFunctionCache; - std::map, EdgeFunction> - SummaryEdgeFunctionCache; + using FlowFunctionType = FlowFunction; + using EdgeFunctionType = EdgeFunction; public: // Ctor allows access to the IDEProblem in order to get access to flow and @@ -189,7 +139,8 @@ class FlowEdgeFunctionCache { FlowEdgeFunctionCache & operator=(FlowEdgeFunctionCache &&FEFC) noexcept = default; - FlowFunctionPtrType getNormalFlowFunction(n_t Curr, n_t Succ) { + [[nodiscard]] NonNullPtr getNormalFlowFunction(n_t Curr, + n_t Succ) { assertNotNull(Curr); assertNotNull(Succ); PAMM_GET_INSTANCE; @@ -198,32 +149,28 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Curr Inst : " << NToString(Curr)); PHASAR_LOG_LEVEL(DEBUG, "(N) Succ Inst : " << NToString(Succ))); auto Key = createEdgeFunctionInstKey(Curr, Succ); - auto SearchNormalFlowFunction = NormalFunctionCache.find(Key); - if (SearchNormalFlowFunction != NormalFunctionCache.end()) { + + // operator[] instead of try_emplace: NormalEdgeFlowData holds both the + // flow function ptr and the edge function map for the same (Curr,Succ) + // key, so getNormalEdgeFunction shares this entry via the same lookup. + auto &NormalFE = NormalFunctionCache[std::move(Key)]; + if (!NormalFE.FlowFuncPtr) { + INC_COUNTER("Normal-FF Construction", 1, Full); + auto FF = Problem.getNormalFlowFunction(Curr, Succ); + NormalFE.FlowFuncPtr = AutoAddZero + ? std::make_unique(std::move(FF), ZV) + : std::move(FF); + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("Normal-FF Cache Hit", 1, Full); - if (SearchNormalFlowFunction->second.FlowFuncPtr != nullptr) { - return SearchNormalFlowFunction->second.FlowFuncPtr; - } - auto FF = (AutoAddZero) - ? std::make_shared>( - Problem.getNormalFlowFunction(Curr, Succ), ZV) - : Problem.getNormalFlowFunction(Curr, Succ); - SearchNormalFlowFunction->second.FlowFuncPtr = FF; - return FF; } - INC_COUNTER("Normal-FF Construction", 1, Full); - auto FF = (AutoAddZero) - ? std::make_shared>( - Problem.getNormalFlowFunction(Curr, Succ), ZV) - : Problem.getNormalFlowFunction(Curr, Succ); - NormalFunctionCache.insert(std::make_pair(Key, NormalEdgeFlowData(FF))); - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - return FF; + return getPointerFrom(NormalFE.FlowFuncPtr); } - FlowFunctionPtrType getCallFlowFunction(n_t CallSite, f_t DestFun) { + [[nodiscard]] NonNullPtr getCallFlowFunction(n_t CallSite, + f_t DestFun) { assertNotNull(CallSite); assertNotNull(DestFun); PAMM_GET_INSTANCE; @@ -232,24 +179,24 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Call Stmt : " << NToString(CallSite)); PHASAR_LOG_LEVEL(DEBUG, "(F) Dest Fun : " << FToString(DestFun))); auto Key = std::tie(CallSite, DestFun); - auto SearchCallFlowFunction = CallFlowFunctionCache.find(Key); - if (SearchCallFlowFunction != CallFlowFunctionCache.end()) { + + auto [It, Inserted] = CallFlowFunctionCache.try_emplace(std::move(Key)); + if (Inserted) { + INC_COUNTER("Call-FF Construction", 1, Full); + auto FF = Problem.getCallFlowFunction(CallSite, DestFun); + It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) + : std::move(FF); + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("Call-FF Cache Hit", 1, Full); - return SearchCallFlowFunction->second; } - INC_COUNTER("Call-FF Construction", 1, Full); - auto FF = (AutoAddZero) - ? std::make_shared>( - Problem.getCallFlowFunction(CallSite, DestFun), ZV) - : Problem.getCallFlowFunction(CallSite, DestFun); - CallFlowFunctionCache.insert(std::make_pair(Key, FF)); - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - return FF; + + return getPointerFrom(It->second); } - FlowFunctionPtrType getRetFlowFunction(n_t CallSite, f_t CalleeFun, - n_t ExitInst, n_t RetSite) { + [[nodiscard]] NonNullPtr + getRetFlowFunction(n_t CallSite, f_t CalleeFun, n_t ExitInst, n_t RetSite) { assertNotNull(CallSite); assertNotNull(CalleeFun); assertNotNull(ExitInst); @@ -262,27 +209,26 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Exit Stmt : " << NToString(ExitInst)); PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite))); auto Key = std::tie(CallSite, CalleeFun, ExitInst, RetSite); - auto SearchReturnFlowFunction = ReturnFlowFunctionCache.find(Key); - if (SearchReturnFlowFunction != ReturnFlowFunctionCache.end()) { + + auto [It, Inserted] = ReturnFlowFunctionCache.try_emplace(std::move(Key)); + if (Inserted) { + INC_COUNTER("Return-FF Construction", 1, Full); + auto FF = + Problem.getRetFlowFunction(CallSite, CalleeFun, ExitInst, RetSite); + It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) + : std::move(FF); + + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("Return-FF Cache Hit", 1, Full); - return SearchReturnFlowFunction->second; } - INC_COUNTER("Return-FF Construction", 1, Full); - auto FF = (AutoAddZero) - ? std::make_shared>( - Problem.getRetFlowFunction(CallSite, CalleeFun, - ExitInst, RetSite), - ZV) - : Problem.getRetFlowFunction(CallSite, CalleeFun, ExitInst, - RetSite); - ReturnFlowFunctionCache.insert(std::make_pair(Key, FF)); - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - return FF; + return getPointerFrom(It->second); } - FlowFunctionPtrType getCallToRetFlowFunction(n_t CallSite, n_t RetSite, - llvm::ArrayRef Callees) { + [[nodiscard]] NonNullPtr + getCallToRetFlowFunction(n_t CallSite, n_t RetSite, + llvm::ArrayRef Callees) { assertNotNull(CallSite); assertNotNull(RetSite); assertAllNotNull(Callees); @@ -293,29 +239,34 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Call Site : " << NToString(CallSite)); PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); PHASAR_LOG_LEVEL(DEBUG, "(F) Callee's : "); - for (auto callee : Callees) { - PHASAR_LOG_LEVEL(DEBUG, " " << FToString(callee)); + for (auto Callee : Callees) { + PHASAR_LOG_LEVEL(DEBUG, " " << FToString(Callee)); };); auto Key = std::tie(CallSite, RetSite); - auto SearchCallToRetFlowFunction = CallToRetFlowFunctionCache.find(Key); - if (SearchCallToRetFlowFunction != CallToRetFlowFunctionCache.end()) { + + auto [It, Inserted] = + CallToRetFlowFunctionCache.try_emplace(std::move(Key)); + if (Inserted) { + INC_COUNTER("CallToRet-FF Construction", 1, Full); + auto FF = Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees); + It->second = AutoAddZero ? std::make_unique(std::move(FF), ZV) + : std::move(FF); + + PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); + } else { PHASAR_LOG_LEVEL(DEBUG, "Flow function fetched from cache"); INC_COUNTER("CallToRet-FF Cache Hit", 1, Full); - return SearchCallToRetFlowFunction->second; } - INC_COUNTER("CallToRet-FF Construction", 1, Full); - auto FF = - (AutoAddZero) - ? std::make_shared>( - Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees), - ZV) - : Problem.getCallToRetFlowFunction(CallSite, RetSite, Callees); - CallToRetFlowFunctionCache.insert(std::make_pair(Key, FF)); - PHASAR_LOG_LEVEL(DEBUG, "Flow function constructed"); - return FF; + + return getPointerFrom(It->second); } - FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, f_t DestFun) { + /// \note Unlike the other get*FlowFunction methods, this returns a nullable + /// FlowFunctionPtrType rather than NonNullPtr. A null return means no special + /// summary is available for this call site; the solver falls back to + /// propagating through the callee body. + [[nodiscard]] FlowFunctionPtrType getSummaryFlowFunction(n_t CallSite, + f_t DestFun) { assertNotNull(CallSite); assertNotNull(DestFun); // PAMM_GET_INSTANCE; @@ -329,8 +280,8 @@ class FlowEdgeFunctionCache { return FF; } - EdgeFunction getNormalEdgeFunction(n_t Curr, d_t CurrNode, n_t Succ, - d_t SuccNode) { + [[nodiscard]] EdgeFunctionType getNormalEdgeFunction(n_t Curr, d_t CurrNode, + n_t Succ, d_t SuccNode) { assertNotNull(Curr); assertNotNull(Succ); @@ -343,41 +294,27 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Succ Node : " << DToString(SuccNode))); EdgeFuncInstKey OuterMapKey = createEdgeFunctionInstKey(Curr, Succ); - auto SearchInnerMap = NormalFunctionCache.find(OuterMapKey); - if (SearchInnerMap != NormalFunctionCache.end()) { - auto SearchEdgeFunc = SearchInnerMap->second.EdgeFunctionMap.find( - createEdgeFunctionNodeKey(CurrNode, SuccNode)); - if (SearchEdgeFunc != SearchInnerMap->second.EdgeFunctionMap.end()) { - INC_COUNTER("Normal-EF Cache Hit", 1, Full); - PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); - PHASAR_LOG_LEVEL(DEBUG, - "Provide Edge Function: " << SearchEdgeFunc->second); - return SearchEdgeFunc->second; - } - INC_COUNTER("Normal-EF Construction", 1, Full); - auto EF = Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); - - SearchInnerMap->second.EdgeFunctionMap.insert( - createEdgeFunctionNodeKey(CurrNode, SuccNode), EF); - - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; - } - INC_COUNTER("Normal-EF Construction", 1, Full); - auto EF = Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); - - NormalFunctionCache.try_emplace( - OuterMapKey, NormalEdgeFlowData(InnerEdgeFunctionMapType{std::make_pair( - createEdgeFunctionNodeKey(CurrNode, SuccNode), EF)})); - - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; + auto &NormalFE = NormalFunctionCache[std::move(OuterMapKey)]; + auto Ret = NormalFE.EdgeFunctionMap.getOrInsertLazy( + createEdgeFunctionNodeKey(CurrNode, SuccNode), + [&] { + INC_COUNTER("Normal-EF Construction", 1, Full); + auto EF = + Problem.getNormalEdgeFunction(Curr, CurrNode, Succ, SuccNode); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + return EF; + }, + [&] { + INC_COUNTER("Normal-EF Cache Hit", 1, Full); + PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); + }); + PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << Ret); + return Ret; } - EdgeFunction getCallEdgeFunction(n_t CallSite, d_t SrcNode, - f_t DestinationFunction, d_t DestNode) { + [[nodiscard]] EdgeFunctionType getCallEdgeFunction(n_t CallSite, d_t SrcNode, + f_t DestinationFunction, + d_t DestNode) { assertNotNull(CallSite); assertNotNull(DestinationFunction); @@ -392,26 +329,26 @@ class FlowEdgeFunctionCache { "(F) Dest Fun : " << FToString(DestinationFunction)); PHASAR_LOG_LEVEL(DEBUG, "(D) Dest Node : " << DToString(DestNode))); auto Key = std::tie(CallSite, SrcNode, DestinationFunction, DestNode); - auto SearchCallEdgeFunction = CallEdgeFunctionCache.find(Key); - if (SearchCallEdgeFunction != CallEdgeFunctionCache.end()) { + + auto [It, Inserted] = CallEdgeFunctionCache.try_emplace(std::move(Key)); + if (Inserted) { + INC_COUNTER("Call-EF Construction", 1, Full); + It->second = Problem.getCallEdgeFunction(CallSite, SrcNode, + DestinationFunction, DestNode); + + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + } else { INC_COUNTER("Call-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); - PHASAR_LOG_LEVEL( - DEBUG, "Provide Edge Function: " << SearchCallEdgeFunction->second); - return SearchCallEdgeFunction->second; } - INC_COUNTER("Call-EF Construction", 1, Full); - auto EF = Problem.getCallEdgeFunction(CallSite, SrcNode, - DestinationFunction, DestNode); - CallEdgeFunctionCache.insert(std::make_pair(Key, EF)); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; + + PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << It->second); + return It->second; } - EdgeFunction getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, - n_t ExitInst, d_t ExitNode, - n_t RetSite, d_t RetNode) { + [[nodiscard]] EdgeFunctionType + getReturnEdgeFunction(n_t CallSite, f_t CalleeFunction, n_t ExitInst, + d_t ExitNode, n_t RetSite, d_t RetNode) { assertNotNull(CallSite); assertNotNull(CalleeFunction); assertNotNull(ExitInst); @@ -429,26 +366,22 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetNode))); auto Key = std::tie(CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); - auto SearchReturnEdgeFunction = ReturnEdgeFunctionCache.find(Key); - if (SearchReturnEdgeFunction != ReturnEdgeFunctionCache.end()) { - INC_COUNTER("Return-EF Cache Hit", 1, Full); - PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); - PHASAR_LOG_LEVEL( - DEBUG, "Provide Edge Function: " << SearchReturnEdgeFunction->second); - return SearchReturnEdgeFunction->second; + auto [It, Inserted] = ReturnEdgeFunctionCache.try_emplace(std::move(Key)); + + if (Inserted) { + INC_COUNTER("Return-EF Construction", 1, Full); + It->second = Problem.getReturnEdgeFunction( + CallSite, CalleeFunction, ExitInst, ExitNode, RetSite, RetNode); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); } - INC_COUNTER("Return-EF Construction", 1, Full); - auto EF = Problem.getReturnEdgeFunction(CallSite, CalleeFunction, ExitInst, - ExitNode, RetSite, RetNode); - ReturnEdgeFunctionCache.insert(std::make_pair(Key, EF)); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; + + PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << It->second); + return It->second; } - EdgeFunction getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, - n_t RetSite, d_t RetSiteNode, - llvm::ArrayRef Callees) { + [[nodiscard]] EdgeFunctionType + getCallToRetEdgeFunction(n_t CallSite, d_t CallNode, n_t RetSite, + d_t RetSiteNode, llvm::ArrayRef Callees) { assertNotNull(CallSite); assertNotNull(RetSite); assertAllNotNull(Callees); @@ -463,49 +396,34 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(N) Ret Site : " << NToString(RetSite)); PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetSiteNode)); PHASAR_LOG_LEVEL(DEBUG, "(F) Callee's : "); - for (auto callee : Callees) { - PHASAR_LOG_LEVEL(DEBUG, " " << FToString(callee)); + for (auto Callee : Callees) { + PHASAR_LOG_LEVEL(DEBUG, " " << FToString(Callee)); }); EdgeFuncInstKey OuterMapKey = createEdgeFunctionInstKey(CallSite, RetSite); - auto SearchInnerMap = CallToRetEdgeFunctionCache.find(OuterMapKey); - if (SearchInnerMap != CallToRetEdgeFunctionCache.end()) { - auto SearchEdgeFunc = SearchInnerMap->second.find( - createEdgeFunctionNodeKey(CallNode, RetSiteNode)); - if (SearchEdgeFunc != SearchInnerMap->second.end()) { - INC_COUNTER("CallToRet-EF Cache Hit", 1, Full); - PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); - PHASAR_LOG_LEVEL(DEBUG, - "Provide Edge Function: " << SearchEdgeFunc->second); - return SearchEdgeFunc->second; - } - INC_COUNTER("CallToRet-EF Construction", 1, Full); - auto EF = Problem.getCallToRetEdgeFunction(CallSite, CallNode, RetSite, - RetSiteNode, Callees); - - SearchInnerMap->second.insert( - createEdgeFunctionNodeKey(CallNode, RetSiteNode), EF); - - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; - } - - INC_COUNTER("CallToRet-EF Construction", 1, Full); - auto EF = Problem.getCallToRetEdgeFunction(CallSite, CallNode, RetSite, - RetSiteNode, Callees); - - CallToRetEdgeFunctionCache.emplace( - OuterMapKey, - InnerEdgeFunctionMapType{std::make_pair( - createEdgeFunctionNodeKey(CallNode, RetSiteNode), EF)}); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; + auto &Outer = CallToRetEdgeFunctionCache[std::move(OuterMapKey)]; + + auto Ret = Outer.getOrInsertLazy( + std::move(createEdgeFunctionNodeKey(CallNode, RetSiteNode)), + [&] { + INC_COUNTER("CallToRet-EF Construction", 1, Full); + auto Ret = Problem.getCallToRetEdgeFunction( + CallSite, CallNode, RetSite, RetSiteNode, Callees); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + return Ret; + }, + [&] { + INC_COUNTER("CallToRet-EF Cache Hit", 1, Full); + PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); + }); + PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << Ret); + return Ret; } - EdgeFunction getSummaryEdgeFunction(n_t CallSite, d_t CallNode, - n_t RetSite, d_t RetSiteNode) { + [[nodiscard]] EdgeFunctionType getSummaryEdgeFunction(n_t CallSite, + d_t CallNode, + n_t RetSite, + d_t RetSiteNode) { assertNotNull(CallSite); assertNotNull(RetSite); @@ -518,21 +436,19 @@ class FlowEdgeFunctionCache { PHASAR_LOG_LEVEL(DEBUG, "(D) Ret Node : " << DToString(RetSiteNode)); PHASAR_LOG_LEVEL(DEBUG, ' ')); auto Key = std::tie(CallSite, CallNode, RetSite, RetSiteNode); - auto SearchSummaryEdgeFunction = SummaryEdgeFunctionCache.find(Key); - if (SearchSummaryEdgeFunction != SummaryEdgeFunctionCache.end()) { + auto [It, Inserted] = SummaryEdgeFunctionCache.try_emplace(std::move(Key)); + if (Inserted) { + INC_COUNTER("Summary-EF Construction", 1, Full); + It->second = Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, + RetSiteNode); + PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); + } else { INC_COUNTER("Summary-EF Cache Hit", 1, Full); PHASAR_LOG_LEVEL(DEBUG, "Edge function fetched from cache"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " - << SearchSummaryEdgeFunction->second); - return SearchSummaryEdgeFunction->second; } - INC_COUNTER("Summary-EF Construction", 1, Full); - auto EF = Problem.getSummaryEdgeFunction(CallSite, CallNode, RetSite, - RetSiteNode); - SummaryEdgeFunctionCache.insert(std::make_pair(Key, EF)); - PHASAR_LOG_LEVEL(DEBUG, "Edge function constructed"); - PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << EF); - return EF; + + PHASAR_LOG_LEVEL(DEBUG, "Provide Edge Function: " << It->second); + return It->second; } void print() { @@ -634,7 +550,38 @@ class FlowEdgeFunctionCache { } private: - inline EdgeFuncInstKey createEdgeFunctionInstKey(n_t Lhs, n_t Rhs) { + using DTKeyCompressorType = std::conditional_t< + std::is_base_of_v>, + LLVMMapKeyCompressor, DefaultMapKeyCompressor>; + using NTKeyCompressorType = std::conditional_t< + std::is_base_of_v>, + LLVMMapKeyCompressor, DefaultMapKeyCompressor>; + + using MapKeyCompressorType = std::conditional_t< + std::is_same_v, + NTKeyCompressorType, + MapKeyCompressorCombinator>; + + using EdgeFuncInstKey = uint64_t; + using EdgeFuncNodeKey = std::conditional_t< + std::is_base_of_v>, uint64_t, + std::pair>; + using InnerEdgeFunctionMapType = + EquivalenceClassMap; + + using ZFF = ZeroedFlowFunction; + + struct NormalEdgeFlowData { + NormalEdgeFlowData() noexcept = default; + NormalEdgeFlowData(FlowFunctionPtrType Val) : FlowFuncPtr(std::move(Val)) {} + NormalEdgeFlowData(InnerEdgeFunctionMapType Map) + : EdgeFunctionMap{std::move(Map)} {} + + FlowFunctionPtrType FlowFuncPtr{}; + InnerEdgeFunctionMapType EdgeFunctionMap{}; + }; + + constexpr EdgeFuncInstKey createEdgeFunctionInstKey(n_t Lhs, n_t Rhs) { uint64_t Val = 0; Val |= KeyCompressor.getCompressedID(Lhs); Val <<= 32; @@ -642,7 +589,7 @@ class FlowEdgeFunctionCache { return Val; } - inline EdgeFuncNodeKey createEdgeFunctionNodeKey(d_t Lhs, d_t Rhs) { + constexpr EdgeFuncNodeKey createEdgeFunctionNodeKey(d_t Lhs, d_t Rhs) { if constexpr (std::is_base_of_v>) { uint64_t Val = 0; Val |= KeyCompressor.getCompressedID(Lhs); @@ -653,6 +600,32 @@ class FlowEdgeFunctionCache { return std::make_pair(Lhs, Rhs); } } + + MapKeyCompressorType KeyCompressor; + + IDETabulationProblem &Problem; + // Auto add zero + bool AutoAddZero; + d_t ZV; + + // Caches for the flow/edge functions + std::map NormalFunctionCache; + + // Caches for the flow functions + std::map, FlowFunctionPtrType> CallFlowFunctionCache; + std::map, FlowFunctionPtrType> + ReturnFlowFunctionCache; + std::map, FlowFunctionPtrType> + CallToRetFlowFunctionCache; + // Caches for the edge functions + std::map, EdgeFunctionType> + CallEdgeFunctionCache; + std::map, EdgeFunctionType> + ReturnEdgeFunctionCache; + std::map + CallToRetEdgeFunctionCache; + std::map, EdgeFunctionType> + SummaryEdgeFunctionCache; }; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h index 944f711707..bc9f71592d 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowFunctionCache.h @@ -123,7 +123,7 @@ template struct FlowFunctionCacheBase { template static constexpr bool needs_cache_v = - std::is_same_v>, T> || + std::is_same_v>, T> || std::is_same_v, T> || (detail::IsFlowFunctionPtr && !std::is_pointer_v); }; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index f5a88a8c57..8a4c512b9c 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -93,7 +93,7 @@ class IDESolver ICF(&assertNotNull(ICF)), SolverConfig(Problem.getIFDSIDESolverConfig()), CachedFlowEdgeFunctions(Problem), AllTop(Problem.allTopFunction()), - JumpFn(std::make_shared>()), + JumpFn(std::make_unique>()), Seeds(Problem.initialSeeds()) {} IDESolver(IDETabulationProblem *Problem, @@ -390,7 +390,7 @@ class IDESolver n_t n = Edge.getTarget(); // a call node; line 14... d_t d2 = Edge.factAtTarget(); - EdgeFunction f = jumpFunction(Edge); + auto f = jumpFunction(Edge); const auto &ReturnSiteNs = ICF->getReturnSitesOfCallAt(n); const auto &Callees = ICF->getCalleesOfCallAt(n); @@ -419,7 +419,7 @@ class IDESolver // for each possible callee for (f_t SCalledProcN : Callees) { // still line 14 // check if a special summary for the called procedure exists - FlowFunctionPtrType SpecialSum = + auto SpecialSum = CachedFlowEdgeFunctions.getSummaryFlowFunction(n, SCalledProcN); // if a special summary is available, treat this as a normal flow @@ -434,9 +434,8 @@ class IDESolver ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); saveEdges(n, ReturnSiteN, d2, Res, ESGEdgeKind::Summary); for (d_t d3 : Res) { - EdgeFunction SumEdgFnE = - CachedFlowEdgeFunctions.getSummaryEdgeFunction(n, d2, - ReturnSiteN, d3); + auto SumEdgFnE = CachedFlowEdgeFunctions.getSummaryEdgeFunction( + n, d2, ReturnSiteN, d3); INC_COUNTER("SpecialSummary-EF Queries", 1, Full); PHASAR_LOG_LEVEL(DEBUG, @@ -451,7 +450,7 @@ class IDESolver } } else { // compute the call-flow function - FlowFunctionPtrType Function = + auto Function = CachedFlowEdgeFunctions.getCallFlowFunction(n, SCalledProcN); INC_COUNTER("FF Queries", 1, Full); container_type Res = computeCallFlowFunction(Function, d1, d2); @@ -495,13 +494,12 @@ class IDESolver for (const TableCell &Entry : endSummary(SP, d3)) { n_t eP = Entry.getRowKey(); d_t d4 = Entry.getColumnKey(); - EdgeFunction fCalleeSummary = Entry.getValue(); + auto fCalleeSummary = Entry.getValue(); // for each return site for (n_t RetSiteN : ReturnSiteNs) { // compute return-flow function - FlowFunctionPtrType RetFunction = - CachedFlowEdgeFunctions.getRetFlowFunction(n, SCalledProcN, - eP, RetSiteN); + auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( + n, SCalledProcN, eP, RetSiteN); INC_COUNTER("FF Queries", 1, Full); const container_type ReturnedFacts = computeReturnFlowFunction( RetFunction, d3, d4, n, Container{d2}); @@ -512,14 +510,12 @@ class IDESolver for (d_t d5 : ReturnedFacts) { // update the caller-side summary function // get call edge function - EdgeFunction f4 = - CachedFlowEdgeFunctions.getCallEdgeFunction( - n, d2, SCalledProcN, d3); + auto f4 = CachedFlowEdgeFunctions.getCallEdgeFunction( + n, d2, SCalledProcN, d3); PHASAR_LOG_LEVEL(DEBUG, "Queried Call Edge Function: " << f4); // get return edge function - EdgeFunction f5 = - CachedFlowEdgeFunctions.getReturnEdgeFunction( - n, SCalledProcN, eP, d4, RetSiteN, d5); + auto f5 = CachedFlowEdgeFunctions.getReturnEdgeFunction( + n, SCalledProcN, eP, d4, RetSiteN, d5); PHASAR_LOG_LEVEL(DEBUG, "Queried Return Edge Function: " << f5); if (SolverConfig.emitESG()) { @@ -538,7 +534,7 @@ class IDESolver << f4); PHASAR_LOG_LEVEL(DEBUG, " (return * calleeSummary * call)"); - EdgeFunction fPrime = IDEProblem.extend( + auto fPrime = IDEProblem.extend( IDEProblem.extend(f4, fCalleeSummary), f5); PHASAR_LOG_LEVEL(DEBUG, " = " << fPrime); d_t d5_restoredCtx = restoreContextOnReturnedFact(n, d2, d5); @@ -559,9 +555,8 @@ class IDESolver // line 17-19 of Naeem/Lhotak/Rodriguez // process intra-procedural flows along call-to-return flow functions for (n_t ReturnSiteN : ReturnSiteNs) { - FlowFunctionPtrType CallToReturnFF = - CachedFlowEdgeFunctions.getCallToRetFlowFunction(n, ReturnSiteN, - Callees); + auto CallToReturnFF = CachedFlowEdgeFunctions.getCallToRetFlowFunction( + n, ReturnSiteN, Callees); INC_COUNTER("FF Queries", 1, Full); container_type ReturnFacts = computeCallToReturnFlowFunction(CallToReturnFF, d1, d2); @@ -570,9 +565,8 @@ class IDESolver HasNoCalleeInformation ? ESGEdgeKind::SkipUnknownFn : ESGEdgeKind::CallToRet); for (d_t d3 : ReturnFacts) { - EdgeFunction EdgeFnE = - CachedFlowEdgeFunctions.getCallToRetEdgeFunction(n, d2, ReturnSiteN, - d3, Callees); + auto EdgeFnE = CachedFlowEdgeFunctions.getCallToRetEdgeFunction( + n, d2, ReturnSiteN, d3, Callees); PHASAR_LOG_LEVEL(DEBUG, "Queried Call-to-Return Edge Function: " << EdgeFnE); if (SolverConfig.emitESG()) { @@ -599,23 +593,22 @@ class IDESolver INC_COUNTER("Process Normal", 1, Full); PHASAR_LOG_LEVEL( DEBUG, "Process normal at target: " << NToString(Edge.getTarget())); - EdgeFunction f = jumpFunction(Edge); + auto f = jumpFunction(Edge); auto [d1, n, d2] = Edge.consume(); const auto &Fun = ICF->getFunctionOf(n); for (const auto nPrime : ICF->getSuccsOf(n)) { - FlowFunctionPtrType FlowFunc = - CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); + auto FlowFunc = CachedFlowEdgeFunctions.getNormalFlowFunction(n, nPrime); INC_COUNTER("FF Queries", 1, Full); const container_type Res = computeNormalFlowFunction(FlowFunc, d1, d2); ADD_TO_HISTOGRAM("Data-flow facts", Res.size(), 1, Full); saveEdges(n, nPrime, d2, Res, ESGEdgeKind::Normal); - for (d_t d3 : Res) { - EdgeFunction g = + for (const d_t &d3 : Res) { + auto g = CachedFlowEdgeFunctions.getNormalEdgeFunction(n, d2, nPrime, d3); PHASAR_LOG_LEVEL(DEBUG, "Queried Normal Edge Function: " << g); - EdgeFunction fPrime = IDEProblem.extend(f, g); + auto fPrime = IDEProblem.extend(f, g); auto DestN = [&, &n = n] { if (auto &&NextUser = getNextUserOrNull(Fun, d3, n)) { @@ -663,11 +656,11 @@ class IDESolver PAMM_GET_INSTANCE; d_t Fact = NAndD.second; for (const f_t Callee : ICF->getCalleesOfCallAt(Stmt)) { - FlowFunctionPtrType CallFlowFunction = + auto CallFlowFunction = CachedFlowEdgeFunctions.getCallFlowFunction(Stmt, Callee); INC_COUNTER("FF Queries", 1, Full); for (const d_t dPrime : CallFlowFunction->computeTargets(Fact)) { - EdgeFunction EdgeFn = CachedFlowEdgeFunctions.getCallEdgeFunction( + auto EdgeFn = CachedFlowEdgeFunctions.getCallEdgeFunction( Stmt, Fact, Callee, dPrime); PHASAR_LOG_LEVEL(DEBUG, "Queried Call Edge Function: " << EdgeFn); if (SolverConfig.emitESG()) { @@ -819,7 +812,7 @@ class IDESolver LookupByTarget.cellSet()) { d_t dPrime = SourceValTargetValAndFunction.getRowKey(); d_t d = SourceValTargetValAndFunction.getColumnKey(); - EdgeFunction fPrime = SourceValTargetValAndFunction.getValue(); + auto fPrime = SourceValTargetValAndFunction.getValue(); l_t TargetVal = val(SP, dPrime); setVal(n, d, IDEProblem.join(val(n, d), @@ -943,7 +936,7 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, "Process exit at target: " << NToString(Edge.getTarget())); n_t n = Edge.getTarget(); // an exit node; line 21... - EdgeFunction f = jumpFunction(Edge); + auto f = jumpFunction(Edge); f_t FunctionThatNeedsSummary = ICF->getFunctionOf(n); d_t d1 = Edge.factAtSource(); d_t d2 = Edge.factAtTarget(); @@ -969,9 +962,8 @@ class IDESolver // for each return site for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(c)) { // compute return-flow function - FlowFunctionPtrType RetFunction = - CachedFlowEdgeFunctions.getRetFlowFunction( - c, FunctionThatNeedsSummary, n, RetSiteC); + auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( + c, FunctionThatNeedsSummary, n, RetSiteC); INC_COUNTER("FF Queries", 1, Full); // for each incoming-call value for (d_t d4 : Entry.second) { @@ -984,13 +976,12 @@ class IDESolver for (d_t d5 : Targets) { // compute composed function // get call edge function - EdgeFunction f4 = CachedFlowEdgeFunctions.getCallEdgeFunction( + auto f4 = CachedFlowEdgeFunctions.getCallEdgeFunction( c, d4, ICF->getFunctionOf(n), d1); PHASAR_LOG_LEVEL(DEBUG, "Queried Call Edge Function: " << f4); // get return edge function - EdgeFunction f5 = - CachedFlowEdgeFunctions.getReturnEdgeFunction( - c, ICF->getFunctionOf(n), n, d2, RetSiteC, d5); + auto f5 = CachedFlowEdgeFunctions.getReturnEdgeFunction( + c, ICF->getFunctionOf(n), n, d2, RetSiteC, d5); PHASAR_LOG_LEVEL(DEBUG, "Queried Return Edge Function: " << f5); if (SolverConfig.emitESG()) { for (auto SP : ICF->getStartPointsOf(ICF->getFunctionOf(n))) { @@ -1005,8 +996,7 @@ class IDESolver PHASAR_LOG_LEVEL(DEBUG, "Compose: " << f5 << " * " << f << " * " << f4); PHASAR_LOG_LEVEL(DEBUG, " (return * function * call)"); - EdgeFunction fPrime = - IDEProblem.extend(IDEProblem.extend(f4, f), f5); + auto fPrime = IDEProblem.extend(IDEProblem.extend(f4, f), f5); PHASAR_LOG_LEVEL(DEBUG, " = " << fPrime); // for each jump function coming into the call, propagate to // return site using the composed function @@ -1014,7 +1004,7 @@ class IDESolver if (RevLookupResult) { for (size_t I = 0; I < RevLookupResult->get().size(); ++I) { auto ValAndFunc = RevLookupResult->get()[I]; - EdgeFunction f3 = ValAndFunc.second; + auto f3 = ValAndFunc.second; if (f3 != AllTop) { d_t d3 = ValAndFunc.first; d_t d5_restoredCtx = restoreContextOnReturnedFact(c, d4, d5); @@ -1051,18 +1041,16 @@ class IDESolver const auto &Callers = ICF->getCallersOf(FunctionThatNeedsSummary); for (n_t Caller : Callers) { for (n_t RetSiteC : ICF->getReturnSitesOfCallAt(Caller)) { - FlowFunctionPtrType RetFunction = - CachedFlowEdgeFunctions.getRetFlowFunction( - Caller, FunctionThatNeedsSummary, n, RetSiteC); + auto RetFunction = CachedFlowEdgeFunctions.getRetFlowFunction( + Caller, FunctionThatNeedsSummary, n, RetSiteC); INC_COUNTER("FF Queries", 1, Full); const container_type Targets = computeReturnFlowFunction( RetFunction, d1, d2, Caller, Container{ZeroValue}); ADD_TO_HISTOGRAM("Data-flow facts", Targets.size(), 1, Full); saveEdges(n, RetSiteC, d2, Targets, ESGEdgeKind::Ret); for (d_t d5 : Targets) { - EdgeFunction f5 = - CachedFlowEdgeFunctions.getReturnEdgeFunction( - Caller, ICF->getFunctionOf(n), n, d2, RetSiteC, d5); + auto f5 = CachedFlowEdgeFunctions.getReturnEdgeFunction( + Caller, ICF->getFunctionOf(n), n, d2, RetSiteC, d5); PHASAR_LOG_LEVEL(DEBUG, "Queried Return Edge Function: " << f5); if (SolverConfig.emitESG()) { IntermediateEdgeFunctions[std::make_tuple(n, d2, RetSiteC, d5)] @@ -1127,14 +1115,13 @@ class IDESolver /// @param d2 The abstraction at the current node /// @return The set of abstractions at the successor node /// - container_type computeNormalFlowFunction(const FlowFunctionPtrType &FlowFunc, - d_t /*d1*/, d_t d2) { + container_type computeNormalFlowFunction(const auto &FlowFunc, d_t /*d1*/, + d_t d2) { return FlowFunc->computeTargets(d2); } - container_type - computeSummaryFlowFunction(const FlowFunctionPtrType &SummaryFlowFunction, - d_t /*d1*/, d_t d2) { + container_type computeSummaryFlowFunction(const auto &SummaryFlowFunction, + d_t /*d1*/, d_t d2) { return SummaryFlowFunction->computeTargets(d2); } @@ -1144,9 +1131,8 @@ class IDESolver /// @param d2 The abstraction at the call site /// @return The set of caller-side abstractions at the callee's start node /// - container_type - computeCallFlowFunction(const FlowFunctionPtrType &CallFlowFunction, - d_t /*d1*/, d_t d2) { + container_type computeCallFlowFunction(const auto &CallFlowFunction, + d_t /*d1*/, d_t d2) { return CallFlowFunction->computeTargets(d2); } @@ -1158,8 +1144,9 @@ class IDESolver /// @param d2 The abstraction at the call site /// @return The set of caller-side abstractions at the return site /// - container_type computeCallToReturnFlowFunction( - const FlowFunctionPtrType &CallToReturnFlowFunction, d_t /*d1*/, d_t d2) { + container_type + computeCallToReturnFlowFunction(const auto &CallToReturnFlowFunction, + d_t /*d1*/, d_t d2) { return CallToReturnFlowFunction->computeTargets(d2); } @@ -1172,10 +1159,9 @@ class IDESolver /// @param callerSideDs The abstractions at the call site /// @return The set of caller-side abstractions at the return site /// - container_type - computeReturnFlowFunction(const FlowFunctionPtrType &RetFlowFunction, - d_t /*d1*/, d_t d2, n_t /*CallSite*/, - const Container & /*CallerSideDs*/) { + container_type computeReturnFlowFunction(const auto &RetFlowFunction, + d_t /*d1*/, d_t d2, n_t /*CallSite*/, + const Container & /*CallerSideDs*/) { return RetFlowFunction->computeTargets(d2); } @@ -1204,7 +1190,7 @@ class IDESolver PHASAR_LOG_LEVEL( DEBUG, "Edge function : " << f << " (result of previous compose)"); - EdgeFunction JumpFnE = [&]() { + auto JumpFnE = [&]() { const auto RevLookupResult = JumpFn->reverseLookup(Target, TargetVal); if (RevLookupResult) { const auto &JumpFnContainer = RevLookupResult->get(); @@ -1219,7 +1205,7 @@ class IDESolver // was found return AllTop; }(); - EdgeFunction fPrime = IDEProblem.combine(JumpFnE, f); + auto fPrime = IDEProblem.combine(JumpFnE, f); bool NewFunction = fPrime != JumpFnE; IF_LOG_LEVEL_ENABLED(DEBUG, { @@ -1887,7 +1873,7 @@ class IDESolver EdgeFunction AllTop; - std::shared_ptr> JumpFn; + std::unique_ptr> JumpFn; std::map, std::vector>> IntermediateEdgeFunctions; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h index fc79c6e2b0..9add6d419e 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolver.h @@ -321,6 +321,10 @@ class IterativeIDESolver } } + // Flow functions are not consulted after Phase I ends; release them early + // to reduce peak memory. Edge functions are kept until + // performValuePropagation clears the whole cache, as they may still be + // needed for summary queries. FECache.clearFlowFunctions(); SourceFactAndFuncToInterJob.clear(); WorkList.clear(); @@ -400,9 +404,9 @@ class IterativeIDESolver void performValuePropagation() { if constexpr (ComputeValues) { - /// NOTE: We can already clear the EFCache here, as we are not querying - /// any edge function in Phase II; The EFs that are in use are kept alive - /// by their shared_ptr + /// NOTE: Safe to clear here: Phase II only reads EdgeFunction values + /// from the JumpFunctions table, which stores them by value. Neither + /// flow functions nor the EdgeFunctionCache are consulted in Phase II. FECache.clear(); submitInitialValues(); diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h index 9dfa44edff..e5a5a1b5b3 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IterativeIDESolverBase.h @@ -27,7 +27,7 @@ class IterativeIDESolverBase { static constexpr bool ComputeValues = StaticSolverConfigTy::ComputeValues; static constexpr bool EnableStatistics = StaticSolverConfigTy::EnableStatistics; - /// NOTE: EdgeFunctionPtrType may be either std::shared_ptr> + /// NOTE: EdgeFunctionPtrType may be either std::unique_ptr> /// or llvm::IntrusiveRefCntPtr> once this is supported using EdgeFunctionPtrType = std::conditional_t; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h index 9e9257fad6..ef7beb2816 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/LLVMFlowFunctions.h @@ -99,7 +99,7 @@ auto mapFactsAlongsideCallSite(const llvm::CallBase *CallSite, [[no_unique_address]] std::decay_t FactConstructor; }; - return std::make_shared(CallSite, PropagateGlobals, + return std::make_unique(CallSite, PropagateGlobals, std::forward(PropagateArgs), std::forward(FactConstructor)); } @@ -205,7 +205,7 @@ mapFactsToCallee(const llvm::CallBase *CallSite, const llvm::Function *DestFun, [[no_unique_address]] std::decay_t FactConstructor; }; - return std::make_shared(CallSite, DestFun, PropagateGlobals, + return std::make_unique(CallSite, DestFun, PropagateGlobals, PropagateZeroToCallee, std::forward(PropagateArgumentWithSource), std::forward(FactConstructor)); @@ -315,7 +315,7 @@ FlowFunctionPtrType mapFactsToCaller( [[no_unique_address]] std::decay_t PostProcess; }; - return std::make_shared( + return std::make_unique( CallSite, ExitInst, PropagateGlobals, std::forward(PropagateParameter), std::forward(PropagateRet), std::forward(FactConstructor), @@ -364,7 +364,7 @@ strongUpdateStore(const llvm::StoreInst *Store, Fn &&GeneratePointerOpIf) { [[no_unique_address]] std::decay_t Pred; }; - return std::make_shared( + return std::make_unique( Store->getPointerOperand(), BasePtrOp, std::forward(GeneratePointerOpIf)); } @@ -389,7 +389,7 @@ strongUpdateStore(const llvm::StoreInst *Store, Fn &&GeneratePointerOpIf) { [[no_unique_address]] std::decay_t Pred; }; - return std::make_shared( + return std::make_unique( Store, std::forward(GeneratePointerOpIf)); } @@ -439,7 +439,7 @@ strongUpdateStore(const llvm::StoreInst *Store) { const llvm::Value *BasePtrOp; }; - return std::make_shared(Store, BasePtrOp); + return std::make_unique(Store, BasePtrOp); } struct StrongUpdateFlow @@ -460,7 +460,7 @@ strongUpdateStore(const llvm::StoreInst *Store) { const llvm::StoreInst *Store; }; - return std::make_shared(Store); + return std::make_unique(Store); } } // namespace psr diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h index 862af3e477..ebe958cb73 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/ExtendedTaintAnalysis/Helpers.h @@ -37,10 +37,10 @@ using LeakMap_t = std::unordered_map>; /// Have an own function for creating a flow/edge-function instance to allow -/// fast migration to memory-management schemes other than std::shared_ptr +/// fast migration to memory-management schemes other than std::unique_ptr template -inline std::shared_ptr makeFF(Args &&...Arguments) { - return std::make_shared(std::forward(Arguments)...); +inline std::unique_ptr makeFF(Args &&...Arguments) { + return std::make_unique(std::forward(Arguments)...); } } // namespace psr::XTaint diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h index fa0984801b..a8ef5cef36 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.h @@ -18,11 +18,11 @@ namespace psr::glca { // struct AllBot { // using type = AllBottom; -// static std::shared_ptr getInstance(); +// static std::unique_ptr getInstance(); // static bool isBot(const EdgeFunction *EdgeFn, // bool NonRec = false); // static bool -// isBot(const std::shared_ptr> &EdgeFn, +// isBot(const std::unique_ptr> &EdgeFn, // bool NonRec = false); // }; diff --git a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h index 608933f3a1..9d61a44ea9 100644 --- a/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h +++ b/include/phasar/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.h @@ -53,22 +53,22 @@ class IDEGeneralizedLCA : public IDETabulationProblem { IDEGeneralizedLCA(const LLVMProjectIRDB *IRDB, const LLVMBasedICFG *ICF, std::vector EntryPoints, size_t MaxSetSize); - std::shared_ptr> getNormalFlowFunction(n_t Curr, - n_t Succ) override; + MaybeUniquePtr> getNormalFlowFunction(n_t Curr, + n_t Succ) override; - std::shared_ptr> getCallFlowFunction(n_t CallStmt, - f_t DestMthd) override; + MaybeUniquePtr> getCallFlowFunction(n_t CallStmt, + f_t DestMthd) override; - std::shared_ptr> getRetFlowFunction(n_t CallSite, - f_t CalleeMthd, - n_t ExitStmt, - n_t RetSite) override; + MaybeUniquePtr> getRetFlowFunction(n_t CallSite, + f_t CalleeMthd, + n_t ExitStmt, + n_t RetSite) override; - std::shared_ptr> + MaybeUniquePtr> getCallToRetFlowFunction(n_t CallSite, n_t RetSite, llvm::ArrayRef Callees) override; - std::shared_ptr> + MaybeUniquePtr> getSummaryFlowFunction(n_t CallStmt, f_t DestMthd) override; InitialSeeds initialSeeds() override; diff --git a/include/phasar/Utils/EquivalenceClassMap.h b/include/phasar/Utils/EquivalenceClassMap.h index 20691aa3cf..b8aa14c2ee 100644 --- a/include/phasar/Utils/EquivalenceClassMap.h +++ b/include/phasar/Utils/EquivalenceClassMap.h @@ -10,6 +10,10 @@ #ifndef PHASAR_UTILS_EQUIVALENCECLASSMAP_H #define PHASAR_UTILS_EQUIVALENCECLASSMAP_H +#include "phasar/Utils/Macros.h" +#include "phasar/Utils/PointerProxy.h" +#include "phasar/Utils/TypeTraits.h" + #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -61,11 +65,9 @@ template struct EquivalenceClassMap { this->insert(Vals.begin(), Vals.end()); } - [[nodiscard]] inline const_iterator begin() const { - return StoredData.begin(); - } - [[nodiscard]] inline const_iterator end() const { return StoredData.end(); } - [[nodiscard]] inline llvm::iterator_range + [[nodiscard]] const_iterator begin() const { return StoredData.begin(); } + [[nodiscard]] const_iterator end() const { return StoredData.end(); } + [[nodiscard]] llvm::iterator_range equivalenceClasses() const { return llvm::make_range(begin(), end()); } @@ -135,8 +137,32 @@ template struct EquivalenceClassMap { return std::make_pair(StoredData.back().first.begin(), true); } + template VCtor, + std::invocable<> OnHitFn = psr::TrueFn> + const ValueT &getOrInsertLazy(KK &&Key, VCtor &&MakeV, OnHitFn &&OnHit = {}) { + for (auto &[StoredKeySet, StoredVal] : StoredData) { + if (StoredKeySet.count(Key)) { + std::invoke(PSR_FWD(OnHit)); + return StoredVal; + } + } + ValueT Val = std::invoke(PSR_FWD(MakeV)); + + for (auto &KVPair : StoredData) { + if (KVPair.second == Val) { + KVPair.first.insert(PSR_FWD(Key)); + // Don't call OnHit() here, since we already needed to construct the + // Val; this is not really a cache hit... + return KVPair.second; + } + } + + StoredData.emplace_back(SetType{Key}, std::move(Val)); + return StoredData.back().second; + } + /// Return 1 if the specified key is in the map, 0 otherwise. - [[nodiscard]] inline size_type count(const KeyT &Key) const { + [[nodiscard]] size_type count(const KeyT &Key) const { for (auto &KVPair : StoredData) { if (KVPair.first.count(Key) >= 1) { return 1; @@ -145,14 +171,12 @@ template struct EquivalenceClassMap { return 0; } - [[nodiscard]] inline size_type numEquivalenceClasses() const { + [[nodiscard]] size_type numEquivalenceClasses() const { return StoredData.size(); } /// Returns the size of the map, i.e., the number of equivalence classes. - [[nodiscard]] inline size_type size() const { - return numEquivalenceClasses(); - } + [[nodiscard]] size_type size() const { return numEquivalenceClasses(); } [[nodiscard]] const_iterator find(key_type Key) const { return llvm::find_if(StoredData, @@ -169,7 +193,7 @@ template struct EquivalenceClassMap { return std::nullopt; } - inline void clear() { StoredData.clear(); } + void clear() { StoredData.clear(); } private: StorageT StoredData{}; @@ -183,11 +207,10 @@ class EquivalenceClassMapNG { public: // NOLINTNEXTLINE(readability-identifier-naming) class const_iterator { - public: using value_type = std::pair; using reference = std::pair; - using pointer = reference *; + using pointer = PointerProxy; using difference_type = ptrdiff_t; using iterator_category = std::forward_iterator_tag; @@ -199,10 +222,7 @@ class EquivalenceClassMapNG { reference operator*() noexcept { return reference(*Ky, *Val); } - pointer operator->() noexcept { - TempStorage.emplace(*Ky, *Val); - return &*TempStorage; - } + pointer operator->() noexcept { return pointer{**this}; } bool operator==(const const_iterator &Other) const noexcept { return Val == Other.Val; @@ -220,8 +240,6 @@ class EquivalenceClassMapNG { const TValue *Val; const SetTy *Ky; - - std::optional TempStorage; }; EquivalenceClassMapNG() noexcept = default; @@ -236,23 +254,25 @@ class EquivalenceClassMapNG { ValueComparator VComp; for (size_t I = 0, End = Values.size(); I != End; ++I) { if (VComp(Values[I], Value)) { - return {getIterator(I), Keys[I].insert(std::forward(Key)).second}; + return {getIterator(I), Keys[I].insert(PSR_FWD(Key)).second}; } } - Values.emplace_back(std::forward(Value)); - Keys.emplace_back().insert(std::forward(Key)); + Values.emplace_back(PSR_FWD(Value)); + Keys.emplace_back().insert(PSR_FWD(Key)); return {getIterator(Values.size() - 1), true}; } - template - const TValue &getOrInsertLazy(KK &&Key, VCtor &&MakeV) { + template VCtor, + std::invocable<> OnHitFn = psr::TrueFn> + const TValue &getOrInsertLazy(KK &&Key, VCtor &&MakeV, OnHitFn &&OnHit = {}) { for (size_t I = 0, End = Keys.size(); I != End; ++I) { if (Keys[I].count(Key)) { + std::invoke(PSR_FWD(OnHit)); return Values[I]; } } - return (*insert(std::forward(Key), std::invoke(MakeV)).first).second; + return (*insert(PSR_FWD(Key), std::invoke(PSR_FWD(MakeV))).first).second; } const_iterator begin() const noexcept { @@ -270,14 +290,12 @@ class EquivalenceClassMapNG { return end(); } - [[nodiscard]] inline size_t numEquivalenceClasses() const noexcept { + [[nodiscard]] size_t numEquivalenceClasses() const noexcept { return Values.size(); } /// Returns the size of the map, i.e., the number of equivalence classes. - [[nodiscard]] inline size_t size() const noexcept { - return numEquivalenceClasses(); - } + [[nodiscard]] size_t size() const noexcept { return numEquivalenceClasses(); } [[nodiscard]] bool empty() const noexcept { return Values.empty(); } diff --git a/include/phasar/Utils/Macros.h b/include/phasar/Utils/Macros.h index 710cd0cff7..74d3c6690f 100644 --- a/include/phasar/Utils/Macros.h +++ b/include/phasar/Utils/Macros.h @@ -27,4 +27,12 @@ #define PSR_DEPRECATED(MSG, REPLACEMENT) [[deprecated(MSG)]] #endif +#if __has_cpp_attribute(clang::lifetimebound) +#define PSR_LIFETIMEBOUND [[clang::lifetimebound]] +#elif __has_cpp_attribute([[lifetimebound]]) +#define PSR_LIFETIMEBOUND [[lifetimebound]] +#else +#define PSR_LIFETIMEBOUND +#endif + #endif // PHASAR_UTILS_MACROS_H diff --git a/include/phasar/Utils/PointerProxy.h b/include/phasar/Utils/PointerProxy.h new file mode 100644 index 0000000000..30a6257574 --- /dev/null +++ b/include/phasar/Utils/PointerProxy.h @@ -0,0 +1,27 @@ +#pragma once + +/****************************************************************************** + * Copyright (c) 2026 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 + *****************************************************************************/ + +#include "phasar/Utils/Macros.h" + +namespace psr { +template struct PointerProxy { + T Value; + + [[nodiscard]] constexpr T *get() noexcept PSR_LIFETIMEBOUND { return &Value; } + [[nodiscard]] constexpr T *operator->() noexcept PSR_LIFETIMEBOUND { + return &Value; + } + + [[nodiscard]] constexpr T &operator*() noexcept PSR_LIFETIMEBOUND { + return Value; + } +}; +} // namespace psr diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp index 2ec25c0bfe..ac7d560020 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEFeatureTaintAnalysis.cpp @@ -271,7 +271,7 @@ auto IDEFeatureTaintAnalysis::getCallToRetFlowFunction( return identityFlow(); } - auto Mapper = mapFactsAlongsideCallSite( + FlowFunctionPtrType Mapper = mapFactsAlongsideCallSite( Call, [RetVal](d_t Arg) { if (RetVal == Arg) { diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.cpp index 6af6b75d72..e6cf6f8138 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/AllBot.cpp @@ -23,9 +23,9 @@ static_assert(psr::IsEqualityComparable>); // namespace psr::glca { -// std::shared_ptr AllBot::getInstance() { -// static std::shared_ptr Ret = -// std::make_shared(IDEGeneralizedLCA::l_t{nullptr}); +// std::unique_ptr AllBot::getInstance() { +// static std::unique_ptr Ret = +// std::make_unique(IDEGeneralizedLCA::l_t{nullptr}); // return Ret; // } @@ -51,7 +51,7 @@ static_assert(psr::IsEqualityComparable>); // } // bool AllBot::isBot( -// const std::shared_ptr> &EdgeFn, +// const std::unique_ptr> &EdgeFn, // bool NonRec) { // return isBot(EdgeFn.get(), NonRec); // } diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.cpp index 8e7a81e2d3..59165dbe52 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IDEGeneralizedLCA/IDEGeneralizedLCA.cpp @@ -53,7 +53,7 @@ IDEGeneralizedLCA::IDEGeneralizedLCA(const LLVMProjectIRDB *IRDB, } // flow functions -std::shared_ptr> +MaybeUniquePtr> IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr, IDEGeneralizedLCA::n_t /*Succ*/) { if (const auto *Store = llvm::dyn_cast(Curr)) { @@ -153,7 +153,7 @@ IDEGeneralizedLCA::getNormalFlowFunction(IDEGeneralizedLCA::n_t Curr, return identityFlow(); } -std::shared_ptr> +MaybeUniquePtr> IDEGeneralizedLCA::getCallFlowFunction(IDEGeneralizedLCA::n_t CallStmt, IDEGeneralizedLCA::f_t DestMthd) { assert(llvm::isa(CallStmt)); @@ -161,11 +161,11 @@ IDEGeneralizedLCA::getCallFlowFunction(IDEGeneralizedLCA::n_t CallStmt, // kill all data-flow facts at calls to string constructors return killAllFlows(); } - return std::make_shared( + return std::make_unique( llvm::cast(CallStmt), DestMthd); } -std::shared_ptr> +MaybeUniquePtr> IDEGeneralizedLCA::getRetFlowFunction(IDEGeneralizedLCA::n_t CallSite, IDEGeneralizedLCA::f_t CalleeMthd, IDEGeneralizedLCA::n_t ExitStmt, @@ -173,16 +173,16 @@ IDEGeneralizedLCA::getRetFlowFunction(IDEGeneralizedLCA::n_t CallSite, assert(llvm::isa(CallSite)); // llvm::outs() << "Ret flow: " << llvmIRToString(ExitStmt) << // std::endl; - /*return std::make_shared( + /*return std::make_unique( llvm::ImmutableCallSite(callSite), calleeMthd, exitStmt, [](const llvm::Value *v) -> bool { return v && v->getType()->isPointerTy(); });*/ - return std::make_shared( + return std::make_unique( llvm::cast(CallSite), ExitStmt, CalleeMthd); } -std::shared_ptr> +MaybeUniquePtr> IDEGeneralizedLCA::getCallToRetFlowFunction(IDEGeneralizedLCA::n_t CallSite, IDEGeneralizedLCA::n_t /*RetSite*/, llvm::ArrayRef /*Callees*/) { @@ -212,7 +212,7 @@ IDEGeneralizedLCA::getCallToRetFlowFunction(IDEGeneralizedLCA::n_t CallSite, return identityFlow(); } -std::shared_ptr> +MaybeUniquePtr> IDEGeneralizedLCA::getSummaryFlowFunction(IDEGeneralizedLCA::n_t /*CallStmt*/, IDEGeneralizedLCA::f_t /*DestMthd*/) { // llvm::outs() << "Summary flow: " << llvmIRToString(callStmt) << @@ -315,7 +315,7 @@ EdgeFunction IDEGeneralizedLCA::getNormalEdgeFunction( // Case II: Storing an integer typed value. /*if (currNode != succNode && valueOperand->getType()->isIntegerTy()) { return IdentityEdgeFunction::getInstance(maxSetSize); - // return std::make_shared(curr, succ, + // return std::make_unique(curr, succ, // maxSetSize); }*/ } @@ -327,7 +327,7 @@ EdgeFunction IDEGeneralizedLCA::getNormalEdgeFunction( // llvm::outs() << "LOAD " << llvmIRToString(curr) << " TO " // << llvmIRToString(succ) << std::endl; // return EdgeIdentity::getInstance(); - // return std::make_shared(curr, succ, + // return std::make_unique(curr, succ, // maxSetSize); return IdentityEdgeFunction::getInstance(maxSetSize); } diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.cpp index c43a9f99ea..9db012b55f 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSTypeAnalysis.cpp @@ -37,7 +37,7 @@ IFDSTypeAnalysis::getNormalFlowFunction(IFDSTypeAnalysis::n_t /*Curr*/, return set{}; } }; - return make_shared(); + return make_unique(); } IFDSTypeAnalysis::FlowFunctionPtrType @@ -49,7 +49,7 @@ IFDSTypeAnalysis::getCallFlowFunction(IFDSTypeAnalysis::n_t /*CallSite*/, return set{}; } }; - return make_shared(); + return make_unique(); } IFDSTypeAnalysis::FlowFunctionPtrType IFDSTypeAnalysis::getRetFlowFunction( @@ -61,7 +61,7 @@ IFDSTypeAnalysis::FlowFunctionPtrType IFDSTypeAnalysis::getRetFlowFunction( return set{}; } }; - return make_shared(); + return make_unique(); } IFDSTypeAnalysis::FlowFunctionPtrType @@ -74,7 +74,7 @@ IFDSTypeAnalysis::getCallToRetFlowFunction(IFDSTypeAnalysis::n_t /*CallSite*/, return set{}; } }; - return make_shared(); + return make_unique(); } IFDSTypeAnalysis::FlowFunctionPtrType diff --git a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp index b0b6e0bc6e..a85f99fcfa 100644 --- a/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp +++ b/lib/PhasarLLVM/DataFlow/IfdsIde/Problems/IFDSUninitializedVariables.cpp @@ -90,7 +90,7 @@ IFDSUninitializedVariables::getNormalFlowFunction( return {}; } }; - return make_shared(func, zerovalue); + return make_unique(func, zerovalue); } */ @@ -165,7 +165,7 @@ IFDSUninitializedVariables::getNormalFlowFunction( return {Source}; } }; - return std::make_shared(Store, UndefValueUses, getZeroValue()); + return std::make_unique(Store, UndefValueUses, getZeroValue()); } if (const auto *Alloc = llvm::dyn_cast(Curr)) { @@ -292,7 +292,7 @@ IFDSUninitializedVariables::getCallFlowFunction( return {Source}; } }; - return std::make_shared(DestFun, CS, getZeroValue()); + return std::make_unique(DestFun, CS, getZeroValue()); } return identityFlow(); } @@ -336,7 +336,7 @@ IFDSUninitializedVariables::getRetFlowFunction( return Ret; } }; - return std::make_shared(CS, ExitStmt); + return std::make_unique(CS, ExitStmt); } // kill everything else return killAllFlows(); diff --git a/lib/Utils/NlohmannLogging.cpp b/lib/Utils/NlohmannLogging.cpp index c145e9df5f..c990b3d3c7 100644 --- a/lib/Utils/NlohmannLogging.cpp +++ b/lib/Utils/NlohmannLogging.cpp @@ -32,7 +32,7 @@ class LLVMOutputAdapter { template > LLVMOutputAdapter(llvm::raw_ostream &OS) - : OA(std::make_shared(OS)) {} + : OA(std::make_unique(OS)) {} operator nlohmann::detail::output_adapter_t() { return OA; }