From 1cef464b8da4b9a925d1b13aaac8c4e9e4c15613 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 18 Dec 2025 18:07:10 +0100 Subject: [PATCH 1/3] Add Nullable annotation to symbol-table getters in ProjectIRDBBase + improve global-lookup --- include/phasar/DB/ProjectIRDBBase.h | 27 +++++++++++++------ .../phasar/PhasarLLVM/DB/LLVMProjectIRDB.h | 2 ++ lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp | 8 +++++- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/include/phasar/DB/ProjectIRDBBase.h b/include/phasar/DB/ProjectIRDBBase.h index 8b68b81910..60e08cd2ab 100644 --- a/include/phasar/DB/ProjectIRDBBase.h +++ b/include/phasar/DB/ProjectIRDBBase.h @@ -10,6 +10,7 @@ #ifndef PHASAR_DB_PROJECTIRDBBASE_H #define PHASAR_DB_PROJECTIRDBBASE_H +#include "phasar/Utils/Nullable.h" #include "phasar/Utils/TypeTraits.h" #include "llvm/ADT/StringRef.h" @@ -63,13 +64,14 @@ template class ProjectIRDBBase { return self().getAllFunctionsImpl(); } - // Returns the function's definition if available, its declaration otherwise. - [[nodiscard]] f_t getFunction(llvm::StringRef FunctionName) const { + // Returns the function if available, nullptr/nullopt otherwise. + [[nodiscard]] Nullable getFunction(llvm::StringRef FunctionName) const { assert(isValid()); return self().getFunctionImpl(FunctionName); } /// Returns the function's definition if available, null otherwise. - [[nodiscard]] f_t getFunctionDefinition(llvm::StringRef FunctionName) const { + [[nodiscard]] Nullable + getFunctionDefinition(llvm::StringRef FunctionName) const { assert(isValid()); return self().getFunctionDefinitionImpl(FunctionName); } @@ -79,8 +81,17 @@ template class ProjectIRDBBase { return self().hasFunctionImpl(FunctionName); } - /// Returns the global variable's definition if available, null otherwise. - [[nodiscard]] g_t + /// Returns the global variable's definition if available, nullptr/nullopt + /// otherwise. + [[nodiscard]] Nullable + getGlobalVariable(llvm::StringRef GlobalVariableName) const { + assert(isValid()); + return self().getGlobalVariableImpl(GlobalVariableName); + } + + /// Returns the global variable's definition if available, nullptr/nullopt + /// otherwise. + [[nodiscard]] Nullable getGlobalVariableDefinition(llvm::StringRef GlobalVariableName) const { assert(isValid()); return self().getGlobalVariableDefinitionImpl(GlobalVariableName); @@ -102,9 +113,9 @@ template class ProjectIRDBBase { return self().getNumFunctionsImpl(); } - /// Returns the instruction to the corresponding Id. Returns nullptr, if there - /// is no instruction for this Id - [[nodiscard]] n_t getInstruction(size_t Id) const { + /// Returns the instruction to the corresponding Id. Returns nullptr/nullopt, + /// if there is no instruction for this Id + [[nodiscard]] Nullable getInstruction(size_t Id) const { assert(isValid()); return self().getInstructionImpl(Id); } diff --git a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h index 9f64625f1f..d079bbd9db 100644 --- a/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h +++ b/include/phasar/PhasarLLVM/DB/LLVMProjectIRDB.h @@ -161,6 +161,8 @@ class LLVMProjectIRDB : public ProjectIRDBBase { return Mod->getFunction(FunctionName) != nullptr; } [[nodiscard]] g_t + getGlobalVariableImpl(llvm::StringRef GlobalVariableName) const; + [[nodiscard]] g_t getGlobalVariableDefinitionImpl(llvm::StringRef GlobalVariableName) const; [[nodiscard]] size_t getNumInstructionsImpl() const noexcept { return IdToInst.size() - IdOffset; diff --git a/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp b/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp index 954d47ee36..25573f201a 100644 --- a/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp +++ b/lib/PhasarLLVM/DB/LLVMProjectIRDB.cpp @@ -320,10 +320,16 @@ LLVMProjectIRDB::getFunctionDefinitionImpl(llvm::StringRef FunctionName) const { return internalGetFunctionDefinition(*Mod, FunctionName); } +[[nodiscard]] const llvm::GlobalVariable * +LLVMProjectIRDB::getGlobalVariableImpl( + llvm::StringRef GlobalVariableName) const { + return Mod->getGlobalVariable(GlobalVariableName, true); +} + [[nodiscard]] const llvm::GlobalVariable * LLVMProjectIRDB::getGlobalVariableDefinitionImpl( llvm::StringRef GlobalVariableName) const { - auto *G = Mod->getGlobalVariable(GlobalVariableName); + const auto *G = getGlobalVariable(GlobalVariableName); if (G && !G->isDeclaration()) { return G; } From 334de7467891a5b798e821096a1661f6c36f0aaa Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 18 Dec 2025 18:17:39 +0100 Subject: [PATCH 2/3] Additional API for CallGraphBase + minor optimization in CallGraph::deserialize() --- include/phasar/ControlFlow/CallGraph.h | 47 ++++++++++------------ include/phasar/ControlFlow/CallGraphBase.h | 33 ++++++++++++++- 2 files changed, 52 insertions(+), 28 deletions(-) diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index 79350de642..67cdc04e38 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -59,30 +59,6 @@ class CallGraph : public CallGraphBase> { FunctionGetter GetFunctionFromName, InstructionGetter GetInstructionFromId); - /// A range of all functions that are vertices in the call-graph. The number - /// of vertex functions can be retrieved by getNumVertexFunctions(). - [[nodiscard]] auto getAllVertexFunctions() const noexcept { - return llvm::make_first_range(CallersOf); - } - - /// A range of all call-sites that are vertices in the call-graph. The number - /// of vertex-callsites can be retrived by getNumVertexCallSites(). - [[nodiscard]] auto getAllVertexCallSites() const noexcept { - return llvm::make_first_range(CalleesAt); - } - - [[nodiscard]] size_t getNumVertexFunctions() const noexcept { - return CallersOf.size(); - } - [[nodiscard]] size_t getNumVertexCallSites() const noexcept { - return CalleesAt.size(); - } - - /// The number of functions within this call-graph - [[nodiscard]] size_t size() const noexcept { return getNumVertexFunctions(); } - - [[nodiscard]] bool empty() const noexcept { return CallersOf.empty(); } - template void printAsJson(llvm::raw_ostream &OS, FunctionIdGetter GetFunctionId, InstIdGetter GetInstructionId) const { @@ -114,7 +90,7 @@ class CallGraph : public CallGraphBase> { Fun2Id.reserve(CallersOf.size()); size_t CurrId = 0; - for (const auto &Fun : getAllVertexFunctions()) { + for (const auto &Fun : this->getAllVertexFunctions()) { OS << CurrId << "[label=\""; OS.write_escaped(std::invoke(GetFunctionLabel, Fun)) << "\"];\n"; Fun2Id[Fun] = CurrId++; @@ -147,6 +123,25 @@ class CallGraph : public CallGraphBase> { return {}; } + /// A range of all functions that are vertices in the call-graph. The number + /// of vertex functions can be retrieved by getNumVertexFunctions(). + [[nodiscard]] auto getAllVertexFunctionsImpl() const noexcept { + return llvm::make_first_range(CallersOf); + } + + /// A range of all call-sites that are vertices in the call-graph. The number + /// of vertex-callsites can be retrived by getNumVertexCallSites(). + [[nodiscard]] auto getAllVertexCallSitesImpl() const noexcept { + return llvm::make_first_range(CalleesAt); + } + + [[nodiscard]] size_t getNumVertexFunctionsImpl() const noexcept { + return CallersOf.size(); + } + [[nodiscard]] size_t getNumVertexCallSitesImpl() const noexcept { + return CalleesAt.size(); + } + // --- StableVector InstVertexOwner; @@ -274,7 +269,7 @@ CallGraph::deserialize(const CallGraphData &PrecomputedCG, "Invalid Call-Instruction Id: " << JId); } - CGBuilder.addCallEdge(CS, Fun); + CGBuilder.addCallEdge(CS, Fun, CEdges); } } return CGBuilder.consumeCallGraph(); diff --git a/include/phasar/ControlFlow/CallGraphBase.h b/include/phasar/ControlFlow/CallGraphBase.h index 1bc44b603c..e4788fd3f5 100644 --- a/include/phasar/ControlFlow/CallGraphBase.h +++ b/include/phasar/ControlFlow/CallGraphBase.h @@ -36,7 +36,8 @@ template class CallGraphBase : public CRTPBase { /// /// NOTE: This function is typically called in a hot part of the analysis and /// should therefore be very fast - [[nodiscard]] decltype(auto) getCalleesOfCallAt(ByConstRef Inst) const + [[nodiscard]] constexpr decltype(auto) + getCalleesOfCallAt(ByConstRef Inst) const noexcept(noexcept(this->self().getCalleesOfCallAtImpl(Inst))) { static_assert( is_iterable_over_v); @@ -45,11 +46,39 @@ template class CallGraphBase : public CRTPBase { /// Returns an iterable range of all possible call-site candidates that may /// call the given function induced by the used call-graph. - [[nodiscard]] decltype(auto) getCallersOf(ByConstRef Fun) const { + [[nodiscard]] constexpr decltype(auto) + getCallersOf(ByConstRef Fun) const { static_assert( is_iterable_over_vself().getCallersOfImpl(Fun)), n_t>); return self().getCallersOfImpl(Fun); } + + /// A range of all functions that are vertices in the call-graph. The number + /// of vertex functions can be retrieved by getNumVertexFunctions(). + [[nodiscard]] constexpr decltype(auto) + getAllVertexFunctions() const noexcept { + return self().getAllVertexFunctionsImpl(); + } + + /// A range of all call-sites that are vertices in the call-graph. The number + /// of vertex-callsites can be retrived by getNumVertexCallSites(). + [[nodiscard]] constexpr auto getAllVertexCallSites() const noexcept { + return self().getAllVertexCallSitesImpl(); + } + + [[nodiscard]] constexpr size_t getNumVertexFunctions() const noexcept { + return self().getNumVertexFunctionsImpl(); + } + [[nodiscard]] constexpr size_t getNumVertexCallSites() const noexcept { + return self().getNumVertexCallSitesImpl(); + } + + /// The number of functions within this call-graph + [[nodiscard]] constexpr size_t size() const noexcept { + return getNumVertexFunctions(); + } + + [[nodiscard]] constexpr bool empty() const noexcept { return size() == 0; } }; } // namespace psr From b75437a69b57d92ff1d004ddfe13ba0cc548f571 Mon Sep 17 00:00:00 2001 From: Fabian Schiebel Date: Thu, 18 Dec 2025 18:24:49 +0100 Subject: [PATCH 3/3] Minor changes in TypeTraits begin/end in TypeTraits should take T& instead of T, because std::begin/end only work with lvalue references. --- include/phasar/Utils/SCCGeneric.h | 10 ++++++---- include/phasar/Utils/TypeTraits.h | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/include/phasar/Utils/SCCGeneric.h b/include/phasar/Utils/SCCGeneric.h index 9c31d325d0..d6cf50efe8 100644 --- a/include/phasar/Utils/SCCGeneric.h +++ b/include/phasar/Utils/SCCGeneric.h @@ -372,8 +372,10 @@ pearce4VisitIt(const G &Graph, typename GraphTraits::vertex_t Start, using OutEdgeRange = decltype(GTraits::outEdges(Graph, std::declval())); - using OutEdgeIterator = decltype(std::begin(std::declval())); - using OutEdgeSentinel = decltype(std::end(std::declval())); + using OutEdgeIterator = + decltype(llvm::adl_begin(std::declval())); + using OutEdgeSentinel = + decltype(llvm::adl_end(std::declval())); struct DfsFrame { Vertex CurrVtx; @@ -401,7 +403,7 @@ pearce4VisitIt(const G &Graph, typename GraphTraits::vertex_t Start, auto &&OutEdges = GTraits::outEdges(Graph, W); Frame = &CallStack.emplace_back( - DfsFrame{W, std::begin(OutEdges), std::end(OutEdges)}); + DfsFrame{W, llvm::adl_begin(OutEdges), llvm::adl_end(OutEdges)}); V = W; } while (Frame->It != Frame->End); @@ -463,7 +465,7 @@ pearce4VisitIt(const G &Graph, typename GraphTraits::vertex_t Start, "the out-edges, but never an owning container by value. Otherwise, " "the DFSFrame iterators may be dangling"); CallStack.emplace_back( - DfsFrame{Start, std::begin(OutEdges), std::end(OutEdges)}); + DfsFrame{Start, llvm::adl_begin(OutEdges), llvm::adl_end(OutEdges)}); } // Simulate the recursion diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 97e324c379..ffff49c225 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -65,7 +65,7 @@ struct variant_idx, T> template struct ElementType { using IteratorTy = - std::decay_t()))>; + std::decay_t()))>; using type = typename std::iterator_traits::value_type; };