diff --git a/include/phasar/Controller/AnalysisController.h b/include/phasar/Controller/AnalysisController.h index 5ded47b64e..7b9e132c4b 100644 --- a/include/phasar/Controller/AnalysisController.h +++ b/include/phasar/Controller/AnalysisController.h @@ -13,159 +13,27 @@ #include "phasar/AnalysisStrategy/Strategies.h" #include "phasar/Controller/AnalysisControllerEmitterOptions.h" #include "phasar/DataFlow/IfdsIde/IFDSIDESolverConfig.h" -#include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" -#include "phasar/DataFlow/IfdsIde/Solver/IFDSSolver.h" -#include "phasar/DataFlow/IfdsIde/SolverResults.h" -#include "phasar/DataFlow/Mono/Solver/InterMonoSolver.h" -#include "phasar/DataFlow/Mono/Solver/IntraMonoSolver.h" -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" -#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/HelperAnalyses.h" -#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" -#include "phasar/PhasarLLVM/SimpleAnalysisConstructor.h" -#include "phasar/PhasarLLVM/TaintConfig/LLVMTaintConfig.h" -#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" #include "phasar/PhasarLLVM/Utils/DataFlowAnalysisType.h" -#include "phasar/Utils/EnumFlags.h" -#include "phasar/Utils/IO.h" -#include "phasar/Utils/Soundness.h" - -#include -#include -#include +#include namespace psr { class AnalysisController { -private: - HelperAnalyses &HA; - std::vector DataFlowAnalyses; - std::vector AnalysisConfigs; - std::vector EntryPoints; - [[maybe_unused]] AnalysisStrategy Strategy; - AnalysisControllerEmitterOptions EmitterOptions = - AnalysisControllerEmitterOptions::None; - std::string ProjectID; - std::filesystem::path ResultDirectory; - IFDSIDESolverConfig SolverConfig; - - /// - /// \brief The maximum length of the CallStrings used in the InterMonoSolver - /// - static const unsigned K = 3; - - void executeDemandDriven(); - - void executeIncremental(); - - void executeModuleWise(); - - void executeVariational(); - - void executeWholeProgram(); - - void emitRequestedHelperAnalysisResults(); - - void executeIFDSUninitVar(); - void executeIFDSConst(); - void executeIFDSTaint(); - void executeIFDSType(); - void executeIFDSSolverTest(); - void executeIFDSFieldSensTaint(); - void executeIDEXTaint(); - void executeIDEOpenSSLTS(); - void executeIDECSTDIOTS(); - void executeIDELinearConst(); - void executeIDESolverTest(); - void executeIDEIIA(); - void executeIntraMonoFullConstant(); - void executeIntraMonoSolverTest(); - void executeInterMonoSolverTest(); - void executeInterMonoTaint(); - - template - void executeMonoAnalysis(ArgTys &&...Args) { - auto Problem = - createAnalysisProblem(HA, std::forward(Args)...); - SolverTy Solver(Problem); - Solver.solve(); - emitRequestedDataFlowResults(Solver); - } - - template - void executeIntraMonoAnalysis(ArgTys &&...Args) { - executeMonoAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeInterMonoAnalysis(ArgTys &&...Args) { - executeMonoAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeIfdsIdeAnalysis(ArgTys &&...Args) { - auto Problem = - createAnalysisProblem(HA, std::forward(Args)...); - SolverTy Solver(Problem, &HA.getICFG()); - Solver.solve(); - emitRequestedDataFlowResults(Solver); - } - - template - void executeIFDSAnalysis(ArgTys &&...Args) { - executeIfdsIdeAnalysis, ProblemTy>( - std::forward(Args)...); - } - - template - void executeIDEAnalysis(ArgTys &&...Args) { - executeIfdsIdeAnalysis, ProblemTy>( - std::forward(Args)...); - } - - LLVMTaintConfig makeTaintConfig(); - - template void emitRequestedDataFlowResults(T &Solver) { - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitTextReport) { - if (!ResultDirectory.empty()) { - if (auto OFS = - openFileStream(ResultDirectory.string() + "/psr-report.txt")) { - Solver.emitTextReport(*OFS); - } - } else { - Solver.emitTextReport(llvm::outs()); - } - } - if (EmitterOptions & - AnalysisControllerEmitterOptions::EmitGraphicalReport) { - if (!ResultDirectory.empty()) { - if (auto OFS = - openFileStream(ResultDirectory.string() + "/psr-report.html")) { - Solver.emitGraphicalReport(*OFS); - } - } else { - Solver.emitGraphicalReport(llvm::outs()); - } - } - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitRawResults) { - if (!ResultDirectory.empty()) { - if (auto OFS = openFileStream(ResultDirectory.string() + - "/psr-raw-results.txt")) { - Solver.dumpResults(*OFS); - } - } else { - Solver.dumpResults(llvm::outs()); - } - } - if (EmitterOptions & AnalysisControllerEmitterOptions::EmitESGAsDot) { - llvm::outs() - << "Front-end support for 'EmitESGAsDot' to be implemented\n"; - } - } - public: + struct ControllerData { + HelperAnalyses *HA{}; + std::vector DataFlowAnalyses; + std::vector AnalysisConfigs; + std::vector EntryPoints; + [[maybe_unused]] AnalysisStrategy Strategy; + AnalysisControllerEmitterOptions EmitterOptions = + AnalysisControllerEmitterOptions::None; + std::string ProjectID; + std::filesystem::path ResultDirectory; + IFDSIDESolverConfig SolverConfig{}; + }; + explicit AnalysisController( HelperAnalyses &HA, std::vector DataFlowAnalyses, std::vector AnalysisConfigs, @@ -175,21 +43,15 @@ class AnalysisController { std::string ProjectID = "default-phasar-project", std::string OutDirectory = ""); - ~AnalysisController() = default; - - AnalysisController(const AnalysisController &) = delete; - AnalysisController(AnalysisController &&) = delete; - AnalysisController &operator=(const AnalysisController &) = delete; - AnalysisController &operator=(const AnalysisController &&) = delete; - - void executeAs(AnalysisStrategy Strategy); - static constexpr bool needsToEmitPTA(AnalysisControllerEmitterOptions EmitterOptions) { return (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsDot) || (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsJson) || (EmitterOptions & AnalysisControllerEmitterOptions::EmitPTAAsText); } + +private: + ControllerData Data; }; } // namespace psr diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h index 72f33565ac..7f8e98df88 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunction.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunction.h @@ -14,6 +14,8 @@ #include "phasar/Utils/ByRef.h" #include "phasar/Utils/TypeTraits.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/Hashing.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/Support/Casting.h" #include "llvm/Support/TypeName.h" @@ -21,6 +23,7 @@ #include "llvm/Support/raw_ostream.h" #include +#include #include #include #include @@ -65,6 +68,12 @@ concept IsEdgeFunction = requires(const T &EF, const EdgeFunction @@ -73,12 +82,9 @@ class EdgeFunctionBase { alignof(ConcreteEF) <= alignof(void *) && std::is_trivially_copyable_v; + using AllocationPolicy = EdgeFunctionAllocationPolicy; + protected: - enum class AllocationPolicy { - SmallObjectOptimized, - DefaultHeapAllocated, - CustomHeapAllocated, - }; struct RefCountedBase { mutable std::atomic_size_t Rc = 0; }; @@ -326,7 +332,7 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// details. /// [[nodiscard]] l_t computeTarget(ByConstRef Source) const { - assert(!!*this && "computeTarget() called on nullptr!"); + assert(isValid() && "computeTarget() called on nullptr!"); return VTAndHeapAlloc.getPointer()->computeTarget(EF, Source); } @@ -357,8 +363,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// SecondEF.computeTarget(FirstEF.computeTarget(x)). [[nodiscard]] static EdgeFunction compose(const EdgeFunction &FirstEF, const EdgeFunction &SecondEF) { - assert(!!FirstEF && "compose() called on LHS nullptr!"); - assert(!!SecondEF && "compose() called on RHS nullptr!"); + assert(FirstEF.isValid() && "compose() called on LHS nullptr!"); + assert(SecondEF.isValid() && "compose() called on RHS nullptr!"); return FirstEF.VTAndHeapAlloc.getPointer()->compose( FirstEF.EF, SecondEF, FirstEF.VTAndHeapAlloc.getInt()); } @@ -398,12 +404,18 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// connected with the value-lattice on l_t [[nodiscard]] static EdgeFunction join(const EdgeFunction &FirstEF, const EdgeFunction &SecondEF) { - assert(!!FirstEF && "join() called on LHS nullptr!"); - assert(!!SecondEF && "join() called on RHS nullptr!"); + assert(FirstEF.isValid() && "join() called on LHS nullptr!"); + assert(SecondEF.isValid() && "join() called on RHS nullptr!"); return FirstEF.VTAndHeapAlloc.getPointer()->join( FirstEF.EF, SecondEF, FirstEF.VTAndHeapAlloc.getInt()); } + [[nodiscard]] bool + referenceEquals(const EdgeFunction &Other) const noexcept { + return VTAndHeapAlloc.getPointer() == Other.VTAndHeapAlloc.getPointer() && + EF == Other.EF; + } + /// Checks for equality of two edge functions. Equality requires exact /// type-equality and value-equality based on operator== of the concrete edge /// functions that are compared. @@ -546,15 +558,17 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { /// Allows for better optimizations in compose and join and should be /// provided, whehever this knowledge is available. [[nodiscard]] bool isConstant() const noexcept { - assert(!!*this && "isConstant() called on nullptr!"); + assert(isValid() && "isConstant() called on nullptr!"); return VTAndHeapAlloc.getPointer()->isConstant(EF); } - /// Performs a null-check. True, iff thie edge function is not null. - [[nodiscard]] explicit operator bool() const noexcept { + [[nodiscard]] bool isValid() const noexcept { return VTAndHeapAlloc.getOpaqueValue(); } + /// Performs a null-check. True, iff thie edge function is not null. + [[nodiscard]] explicit operator bool() const noexcept { return isValid(); } + /// Performs a runtime-typecheck. True, if the concrete type of the held edge /// function *exactly* equals ConcreteEF. /// @@ -609,6 +623,10 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return VTAndHeapAlloc.getInt() == AllocationPolicy::CustomHeapAllocated; } + [[nodiscard]] auto getAllocationPolicy() const noexcept { + return VTAndHeapAlloc.getInt(); + } + /// 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; } @@ -630,6 +648,36 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { return static_cast *>(EF)->Cache; } + [[nodiscard]] size_t getHashCode() const noexcept { + if (!VTAndHeapAlloc.getOpaqueValue()) { + return 0; + } + + return VTAndHeapAlloc.getPointer()->getHashCode( + EF, VTAndHeapAlloc.getPointer()); + } + + [[nodiscard]] auto depth() const noexcept { + assert(isValid() && "depth() called on nullptr!"); + return VTAndHeapAlloc.getPointer()->depth(EF); + } + + friend size_t hash_value(const EdgeFunction &EF) noexcept { // NOLINT + return EF.getHashCode(); + } + + static EdgeFunction getEmptyKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getEmptyKey(), + AllocationPolicy::SmallObjectOptimized}); + } + + static EdgeFunction getTombstoneKey() noexcept { + return EdgeFunction(nullptr, + {llvm::DenseMapInfo::getTombstoneKey(), + AllocationPolicy::SmallObjectOptimized}); + } + private: struct VTable { // NOLINTBEGIN(readability-identifier-naming) @@ -641,6 +689,8 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { void (*print)(const void *, llvm::raw_ostream &); bool (*isConstant)(const void *) noexcept; void (*destroy)(const void *, AllocationPolicy) noexcept; + size_t (*getHashCode)(const void *, const void *) noexcept; + size_t (*depth)(const void *) noexcept; // NOLINTEND(readability-identifier-naming) }; @@ -704,6 +754,23 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { } } }, + [](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; + } + }, }; // Utility ctor for (copy) construction. Increments the ref-count if @@ -729,6 +796,32 @@ class [[clang::trivial_abi]] EdgeFunction final : EdgeFunctionBase { namespace llvm { +template struct DenseMapInfo> { + static inline auto getEmptyKey() noexcept { + return psr::EdgeFunction::getEmptyKey(); + } + static inline auto getTombstoneKey() noexcept { + return psr::EdgeFunction::getTombstoneKey(); + } + static inline auto getHashValue(const psr::EdgeFunction &EF) noexcept { + return EF.getHashCode(); + } + static inline auto isEqual(const psr::EdgeFunction &EF1, + const psr::EdgeFunction &EF2) noexcept { + if (EF1.referenceEquals(EF2)) { + return true; + } + auto Empty = getEmptyKey(); + auto Tombstone = getTombstoneKey(); + if (EF1.referenceEquals(Empty) || EF2.referenceEquals(Empty) || + EF1.referenceEquals(Tombstone) || EF2.referenceEquals(Tombstone)) { + return false; + } + + return EF1 == EF2; + } +}; + // LLVM is currently overhauling its casting system. Use the new variant once // possible! // Note: The new variant (With CastInfo) is not tested yet! diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h new file mode 100644 index 0000000000..448188b48b --- /dev/null +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionStats.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * Copyright (c) 2023 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H +#define PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +enum class EdgeFunctionKind; +enum class EdgeFunctionAllocationPolicy; + +namespace detail { +struct EdgeFunctionStatsData { + static constexpr size_t NumEFKinds = 5; + static constexpr size_t NumAllocPolicies = 3; + + std::array UniqueEFCount{}; + std::array TotalEFCount{}; + std::array PerAllocCount{}; + size_t MaxDepth{}; + double AvgDepth{}; + double AvgUniqueDepth{}; + + size_t TotalNumJF{}; + size_t UniqueNumJF{}; + size_t NumJFObjects{}; + size_t MaxJFDepth{}; + double AvgJFDepth{}; + double AvgUniqueJFDepth{}; + double AvgJFObjDepth{}; + std::array PerAllocJFCount{}; +}; +} // namespace detail + +class EdgeFunctionStats : public detail::EdgeFunctionStatsData { +public: + [[nodiscard]] size_t getNumUniqueEFs() const noexcept { + return std::reduce(UniqueEFCount.begin(), UniqueEFCount.end()); + } + [[nodiscard]] size_t getNumUniqueEFs(EdgeFunctionKind Kind) const noexcept { + assert(size_t(Kind) < NumEFKinds); + return UniqueEFCount[size_t(Kind)]; // NOLINT + } + + [[nodiscard]] size_t getNumEFs() const noexcept { + return std::reduce(TotalEFCount.begin(), TotalEFCount.end()); + } + [[nodiscard]] size_t getNumEFs(EdgeFunctionKind Kind) const noexcept { + assert(size_t(Kind) < NumEFKinds); + return TotalEFCount[size_t(Kind)]; // NOLINT + } + + [[nodiscard]] size_t getNumEFsPerAllocationPolicy( + EdgeFunctionAllocationPolicy Policy) const noexcept { + assert(size_t(Policy) < NumAllocPolicies); + return PerAllocCount[size_t(Policy)]; // NOLINT + } + + friend llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, + const EdgeFunctionStats &S); + +private: + template + friend class IDESolver; + + constexpr EdgeFunctionStats( + const detail::EdgeFunctionStatsData &Data) noexcept + : detail::EdgeFunctionStatsData(Data) {} +}; +} // namespace psr + +#endif // PHASAR_DATAFLOW_IFDSIDE_EDGEFUNCTIONSTATS_H diff --git a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h index 018a7849fe..de9db609a6 100644 --- a/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h +++ b/include/phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h @@ -13,6 +13,7 @@ #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/JoinLattice.h" +#include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/ArrayRef.h" @@ -265,12 +266,18 @@ template struct EdgeFunctionComposer { return LHS.First == RHS.First && LHS.Second == RHS.Second; } + [[nodiscard]] size_t depth() const noexcept { + return First.depth() + Second.depth(); + } + // -- data members EdgeFunction First{}; EdgeFunction Second{}; }; +static_assert(HasDepth>); + template struct JoinEdgeFunction { using l_t = L; using JLattice = JoinLatticeTraits; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h index b9d89a88ea..637eaa083c 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/FlowEdgeFunctionCache.h @@ -32,6 +32,10 @@ class Value; } // namespace llvm namespace psr { + +enum class EdgeFunctionKind { Normal, Call, Return, CallToReturn, Summary }; +static constexpr size_t EdgeFunctionKindCount = 5; + template class DefaultMapKeyCompressor { public: using KeyType = KeyT; @@ -603,6 +607,32 @@ class FlowEdgeFunctionCache { } } + template void foreachCachedEdgeFunction(Handler Fn) const { + for (const auto &[Key, NormalFns] : NormalFunctionCache) { + for (const auto &[Set, EF] : NormalFns.EdgeFunctionMap) { + std::invoke(Fn, EF, EdgeFunctionKind::Normal); + } + } + + for (const auto &[Key, EF] : CallEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Call); + } + + for (const auto &[Key, EF] : ReturnEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Return); + } + + for (const auto &[Key, CTRFns] : CallToRetEdgeFunctionCache) { + for (const auto &[Set, EF] : CTRFns) { + std::invoke(Fn, EF, EdgeFunctionKind::CallToReturn); + } + } + + for (const auto &[Key, EF] : SummaryEdgeFunctionCache) { + std::invoke(Fn, EF, EdgeFunctionKind::Summary); + } + } + private: inline EdgeFuncInstKey createEdgeFunctionInstKey(n_t Lhs, n_t Rhs) { uint64_t Val = 0; diff --git a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h index eb2c323c3a..3274ced1a8 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/IDESolver.h @@ -20,6 +20,7 @@ #include "phasar/Config/Configuration.h" #include "phasar/DB/ProjectIRDBBase.h" #include "phasar/DataFlow/IfdsIde/EdgeFunction.h" +#include "phasar/DataFlow/IfdsIde/EdgeFunctionStats.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" #include "phasar/DataFlow/IfdsIde/EdgeFunctions.h" #include "phasar/DataFlow/IfdsIde/FlowFunctions.h" @@ -33,6 +34,7 @@ #include "phasar/DataFlow/IfdsIde/Solver/PathEdge.h" #include "phasar/DataFlow/IfdsIde/SolverResults.h" #include "phasar/Domain/AnalysisDomain.h" +#include "phasar/Utils/Average.h" #include "phasar/Utils/DOTGraph.h" #include "phasar/Utils/JoinLattice.h" #include "phasar/Utils/Logger.h" @@ -40,6 +42,7 @@ #include "phasar/Utils/Table.h" #include "phasar/Utils/Utilities.h" +#include "llvm/ADT/DenseSet.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/raw_ostream.h" @@ -256,6 +259,81 @@ class IDESolver std::move(ZeroValue)); } + [[nodiscard]] EdgeFunctionStats getEdgeFunctionStatistics() const { + detail::EdgeFunctionStatsData Stats{}; + + // Cached Edge Functions + { + std::array>, EdgeFunctionKindCount> + UniqueEFs{}; + Sampler DepthSampler{}; + Sampler UniqueDepthSampler{}; + // TODO: Cache EFs + CachedFlowEdgeFunctions.foreachCachedEdgeFunction( + [&](EdgeFunction EF, EdgeFunctionKind Kind) { + auto Depth = EF.depth(); + DepthSampler.addSample(Depth); + if (Depth > Stats.MaxDepth) { + Stats.MaxDepth = Depth; + } + + if (UniqueEFs[size_t(Kind)].insert(std::move(EF)).second) { + UniqueDepthSampler.addSample(Depth); + } + Stats.TotalEFCount[size_t(Kind)]++; + Stats.PerAllocCount[size_t(EF.getAllocationPolicy())]++; + }); + + size_t TotalUniqueNumEF = 0; + for (size_t I = 0, End = UniqueEFs.size(); I != End; ++I) { + Stats.UniqueEFCount[I] = UniqueEFs[I].size(); // NOLINT + TotalUniqueNumEF += UniqueEFs[I].size(); + } + + Stats.AvgDepth = DepthSampler.getAverage(); + Stats.AvgUniqueDepth = UniqueDepthSampler.getAverage(); + } + + // Jump Functions + { + llvm::DenseSet> UniqueJumpFns; + llvm::DenseSet AllocatedJumpFns; + Sampler DepthSampler{}; + Sampler UniqueDepthSampler{}; + Sampler AllocDepthSampler{}; + + JumpFn->foreachEdgeFunction([&](EdgeFunction JF) { + ++Stats.TotalNumJF; + auto Depth = JF.depth(); + DepthSampler.addSample(Depth); + if (Depth > Stats.MaxJFDepth) { + Stats.MaxJFDepth = Depth; + } + + if (AllocatedJumpFns.insert(JF.getOpaqueValue()).second) { + AllocDepthSampler.addSample(Depth); + } + + Stats.PerAllocJFCount[size_t(JF.getAllocationPolicy())]++; + + if (UniqueJumpFns.insert(std::move(JF)).second) { + UniqueDepthSampler.addSample(Depth); + } + }); + + Stats.UniqueNumJF = UniqueJumpFns.size(); + Stats.NumJFObjects = AllocatedJumpFns.size(); + Stats.AvgJFDepth = DepthSampler.getAverage(); + Stats.AvgUniqueJFDepth = UniqueDepthSampler.getAverage(); + Stats.AvgJFObjDepth = AllocDepthSampler.getAverage(); + } + return Stats; + } + + void printEdgeFunctionStatistics(llvm::raw_ostream &OS = llvm::outs()) const { + OS << getEdgeFunctionStatistics() << '\n'; + } + protected: /// Lines 13-20 of the algorithm; processing a call site in the caller's /// context. diff --git a/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h b/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h index 0a9bcb3274..85a80fe604 100644 --- a/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h +++ b/include/phasar/DataFlow/IfdsIde/Solver/JumpFunctions.h @@ -18,6 +18,7 @@ #define PHASAR_DATAFLOW_IFDSIDE_SOLVER_JUMPFUNCTIONS_H #include "phasar/DataFlow/IfdsIde/EdgeFunctionUtils.h" +#include "phasar/Utils/ByRef.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Table.h" @@ -157,6 +158,18 @@ template class JumpFunctions { return NonEmptyLookupByTargetNode[Target]; } + template + void foreachEdgeFunction(HandlerFn Handler) const { + NonEmptyForwardLookup.foreachCell( + [Handler = std::move(Handler)](ByConstRef /*Row*/, + ByConstRef /*Col*/, + const auto &TargetFactAndEF) { + for (const auto &[TargetFact, EF] : TargetFactAndEF) { + std::invoke(Handler, EF); + } + }); + } + /** * Removes a jump function. The source statement is implicit. * @see PathEdge diff --git a/include/phasar/Utils/Average.h b/include/phasar/Utils/Average.h new file mode 100644 index 0000000000..c6e54f0901 --- /dev/null +++ b/include/phasar/Utils/Average.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * 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 + *****************************************************************************/ + +#ifndef PHASAR_UTILS_AVERAGE_H +#define PHASAR_UTILS_AVERAGE_H + +#include + +namespace psr { + +struct Sampler { + size_t Count{}; + double Curr{}; + + constexpr void addSample(size_t Sample) noexcept { + Curr += (double(Sample) - Curr) / double(++Count); + } + + [[nodiscard]] constexpr double getAverage() const noexcept { return Curr; } + [[nodiscard]] constexpr size_t getNumSamples() const noexcept { + return Count; + } +}; + +} // namespace psr + +#endif // PHASAR_UTILS_AVERAGE_H diff --git a/include/phasar/Utils/Timer.h b/include/phasar/Utils/Timer.h new file mode 100644 index 0000000000..f485aa0cba --- /dev/null +++ b/include/phasar/Utils/Timer.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) 2023 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_UTILS_TIMER_H +#define PHASAR_UTILS_TIMER_H + +#include "llvm/ADT/FunctionExtras.h" + +#include + +namespace psr { +class Timer { +public: + Timer(llvm::unique_function + WithElapsed) noexcept + : WithElapsed(std::move(WithElapsed)), + Start(std::chrono::steady_clock::now()) {} + + Timer(Timer &&) noexcept = default; + Timer &operator=(Timer &&) noexcept = default; + Timer(const Timer &) = delete; + Timer &operator=(const Timer &) = delete; + + ~Timer() { + if (WithElapsed) { + auto End = std::chrono::steady_clock::now(); + WithElapsed(End - Start); + } + } + +private: + llvm::unique_function WithElapsed; + std::chrono::steady_clock::time_point Start; +}; +} // namespace psr + +#endif // PHASAR_UTILS_TIMER_H diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index e954dd14e6..f4c6736f28 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -112,6 +112,10 @@ struct is_llvm_hashable : std::false_type {}; // NOLINT template struct is_llvm_hashable()))> // NOLINT : std::true_type {}; +template +struct is_llvm_hashable()))> // NOLINT + : std::true_type {}; template