diff --git a/include/phasar/ControlFlow/CFGBase.h b/include/phasar/ControlFlow/CFGBase.h index 4067f05c1..4abfbd983 100644 --- a/include/phasar/ControlFlow/CFGBase.h +++ b/include/phasar/ControlFlow/CFGBase.h @@ -131,7 +131,8 @@ template class CFGBase { void print(ByConstRef Fun, llvm::raw_ostream &OS) const { self().printImpl(Fun, OS); } - [[nodiscard]] nlohmann::json getAsJson(ByConstRef Fun) const { + [[nodiscard, deprecated("Please use printAsJson() instead")]] nlohmann::json + getAsJson(ByConstRef Fun) const { return self().getAsJsonImpl(Fun); } diff --git a/include/phasar/ControlFlow/CallGraph.h b/include/phasar/ControlFlow/CallGraph.h index 590b16964..d73a1b578 100644 --- a/include/phasar/ControlFlow/CallGraph.h +++ b/include/phasar/ControlFlow/CallGraph.h @@ -11,18 +11,18 @@ #define PHASAR_CONTROLFLOW_CALLGRAPH_H #include "phasar/ControlFlow/CallGraphBase.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/Utils/ByRef.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/StableVector.h" #include "phasar/Utils/Utilities.h" -#include "llvm/ADT/ArrayRef.h" -#include "llvm/ADT/DenseMap.h" -#include "llvm/ADT/STLExtras.h" +#include "llvm/IR/Function.h" #include "nlohmann/json.hpp" #include +#include #include #include @@ -58,7 +58,7 @@ class CallGraph : public CallGraphBase> { /// Deserializes a previously computed call-graph template [[nodiscard]] static CallGraph - deserialize(const nlohmann::json &PrecomputedCG, + deserialize(const CallGraphData &PrecomputedCG, FunctionGetter GetFunctionFromName, InstructionGetter GetInstructionFromId); @@ -86,12 +86,33 @@ class CallGraph : public CallGraphBase> { [[nodiscard]] bool empty() const noexcept { return CallersOf.empty(); } + template + void printAsJson(llvm::raw_ostream &OS, FunctionIdGetter GetFunctionId, + InstIdGetter GetInstructionId) const { + CallGraphData CGData; + CGData.FToFunctionVertexTy.reserve(CallersOf.size()); + + for (const auto &[Fun, Callers] : CallersOf) { + auto &JCallers = + CGData.FToFunctionVertexTy[std::invoke(GetFunctionId, Fun)]; + + CGData.FToFunctionVertexTy.reserve(Callers->size()); + for (const auto &CS : *Callers) { + JCallers.push_back(std::invoke(GetInstructionId, CS)); + } + } + + CGData.printAsJson(OS); + } + /// Creates a JSON representation of this call-graph suitable for presistent /// storage. /// Use the ctor taking a json object for deserialization template - [[nodiscard]] nlohmann::json getAsJson(FunctionIdGetter GetFunctionId, - InstIdGetter GetInstructionId) const { + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson(FunctionIdGetter GetFunctionId, + InstIdGetter GetInstructionId) const { nlohmann::json J; for (const auto &[Fun, Callers] : CallersOf) { @@ -254,18 +275,13 @@ template class CallGraphBuilder { template template [[nodiscard]] CallGraph -CallGraph::deserialize(const nlohmann::json &PrecomputedCG, +CallGraph::deserialize(const CallGraphData &PrecomputedCG, FunctionGetter GetFunctionFromName, InstructionGetter GetInstructionFromId) { - if (!PrecomputedCG.is_object()) { - PHASAR_LOG_LEVEL_CAT(ERROR, "CallGraph", "Invalid Json. Expected object"); - return {}; - } - CallGraphBuilder CGBuilder; - CGBuilder.reserve(PrecomputedCG.size()); + CGBuilder.reserve(PrecomputedCG.FToFunctionVertexTy.size()); - for (const auto &[FunName, CallerIDs] : PrecomputedCG.items()) { + for (const auto &[FunName, CallerIDs] : PrecomputedCG.FToFunctionVertexTy) { const auto &Fun = std::invoke(GetFunctionFromName, FunName); if (!Fun) { PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", @@ -277,11 +293,10 @@ CallGraph::deserialize(const nlohmann::json &PrecomputedCG, CEdges->reserve(CallerIDs.size()); for (const auto &JId : CallerIDs) { - auto Id = JId.get(); - const auto &CS = std::invoke(GetInstructionFromId, Id); + const auto &CS = std::invoke(GetInstructionFromId, JId); if (!CS) { PHASAR_LOG_LEVEL_CAT(WARNING, "CallGraph", - "Invalid CAll-Instruction Id: " << Id); + "Invalid Call-Instruction Id: " << JId); } CGBuilder.addCallEdge(CS, Fun); diff --git a/include/phasar/ControlFlow/CallGraphData.h b/include/phasar/ControlFlow/CallGraphData.h new file mode 100644 index 000000000..ed50d66e4 --- /dev/null +++ b/include/phasar/ControlFlow/CallGraphData.h @@ -0,0 +1,34 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_CALLGRAPHDATA_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_CALLGRAPHDATA_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +struct CallGraphData { + // Mangled FunName --> [CS-IDs] + std::unordered_map> FToFunctionVertexTy{}; + + CallGraphData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static CallGraphData deserializeJson(const llvm::Twine &Path); + static CallGraphData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_CALLGRAPHDATA_H diff --git a/include/phasar/ControlFlow/ICFGBase.h b/include/phasar/ControlFlow/ICFGBase.h index b2738c5db..bbdd5b75f 100644 --- a/include/phasar/ControlFlow/ICFGBase.h +++ b/include/phasar/ControlFlow/ICFGBase.h @@ -106,8 +106,16 @@ template class ICFGBase { void print(llvm::raw_ostream &OS = llvm::outs()) const { self().printImpl(OS); } + + /// Prints the underlying call-graph as Json to the given output-stream + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const { + self().printAsJsonImpl(OS); + } + /// Returns the underlying call-graph as JSON - [[nodiscard]] nlohmann::json getAsJson() const { + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const { return self().getAsJsonImpl(); } diff --git a/include/phasar/PhasarLLVM/ControlFlow.h b/include/phasar/PhasarLLVM/ControlFlow.h index 0f4b30135..5ab99e536 100644 --- a/include/phasar/PhasarLLVM/ControlFlow.h +++ b/include/phasar/PhasarLLVM/ControlFlow.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_CONTROLFLOW_H #define PHASAR_PHASARLLVM_CONTROLFLOW_H +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardCFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" diff --git a/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h b/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h new file mode 100644 index 000000000..165bc2022 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H +#define PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Function.h" + +#include +#include + +namespace psr { +class LLVMProjectIRDB; + +[[nodiscard]] std::vector +getEntryFunctions(const LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints); + +[[nodiscard]] std::vector +getEntryFunctionsMut(LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_UTILS_ENTRYFUNCTIONUTILS_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h new file mode 100644 index 000000000..348f341f4 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h @@ -0,0 +1,45 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/IR/Function.h" + +namespace psr { +class LLVMProjectIRDB; + +class GlobalCtorsDtorsModel { +public: + static constexpr llvm::StringLiteral ModelName = + "__psrCRuntimeGlobalCtorsModel"; + + static constexpr llvm::StringLiteral DtorModelName = + "__psrCRuntimeGlobalDtorsModel"; + + static constexpr llvm::StringLiteral DtorsCallerName = + "__psrGlobalDtorsCaller"; + + static constexpr llvm::StringLiteral UserEntrySelectorName = + "__psrCRuntimeUserEntrySelector"; + + static llvm::Function * + buildModel(LLVMProjectIRDB &IRDB, + llvm::ArrayRef UserEntryPoints); + static llvm::Function * + buildModel(LLVMProjectIRDB &IRDB, + llvm::ArrayRef UserEntryPoints); + + /// Returns true, if a function was generated by phasar. + [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &F) noexcept; +}; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_GLOBALCTORSDTORSMODEL_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h index 1384fb64c..c3ac4191b 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.h @@ -44,6 +44,8 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, using CFGBase::print; using ICFGBase::print; + using ICFGBase::printAsJson; + using CFGBase::getAsJson; using ICFGBase::getAsJson; @@ -64,7 +66,8 @@ class LLVMBasedBackwardICFG : public LLVMBasedBackwardCFG, [[nodiscard]] llvm::SmallVector getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; - [[nodiscard]] nlohmann::json getAsJsonImpl() const; + void printAsJsonImpl(llvm::raw_ostream &OS) const; + [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept; [[nodiscard]] size_t getNumCallSitesImpl() const noexcept; diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h new file mode 100644 index 000000000..05d1c34c3 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h @@ -0,0 +1,25 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H + +#include "phasar/ControlFlow/CallGraph.h" + +namespace llvm { +class Instruction; +class Function; +} // namespace llvm + +namespace psr { +using LLVMBasedCallGraph = + CallGraph; +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPH_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h new file mode 100644 index 000000000..bcce15544 --- /dev/null +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h @@ -0,0 +1,49 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Fabian Schiebel and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H +#define PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H + +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" +#include "phasar/Utils/Soundness.h" + +namespace psr { +class LLVMProjectIRDB; +enum class CallGraphAnalysisType; +class LLVMTypeHierarchy; +class LLVMVFTableProvider; +class Resolver; + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(LLVMProjectIRDB &IRDB, CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, + LLVMTypeHierarchy &TH, LLVMVFTableProvider &VTP, + LLVMAliasInfoRef PT = nullptr, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(const LLVMProjectIRDB &IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(LLVMProjectIRDB &IRDB, CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, + LLVMTypeHierarchy &TH, LLVMVFTableProvider &VTP, + LLVMAliasInfoRef PT = nullptr, + Soundness S = Soundness::Soundy); + +[[nodiscard]] LLVMBasedCallGraph +buildLLVMBasedCallGraph(const LLVMProjectIRDB &IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S = Soundness::Soundy); +} // namespace psr + +#endif // PHASAR_PHASARLLVM_CONTROLFLOW_LLVMBASEDCALLGRAPHBUILDER_H diff --git a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h index ca1c48971..333ce033f 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h +++ b/include/phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h @@ -20,10 +20,13 @@ #include "phasar/ControlFlow/CallGraph.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" #include "phasar/ControlFlow/ICFGBase.h" +#include "phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h" #include "phasar/PhasarLLVM/Pointer/LLVMAliasInfo.h" #include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" +#include "phasar/Utils/MaybeUniquePtr.h" #include "phasar/Utils/Soundness.h" #include "llvm/ADT/ArrayRef.h" @@ -33,8 +36,6 @@ #include "llvm/IR/Value.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include namespace psr { @@ -48,20 +49,10 @@ template <> struct CFGTraits : CFGTraits {}; class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { friend ICFGBase; - struct Builder; - public: + // For backward compatibility static constexpr llvm::StringLiteral GlobalCRuntimeModelName = - "__psrCRuntimeGlobalCtorsModel"; - - static constexpr llvm::StringLiteral GlobalCRuntimeDtorModelName = - "__psrCRuntimeGlobalDtorsModel"; - - static constexpr llvm::StringLiteral GlobalCRuntimeDtorsCallerName = - "__psrGlobalDtorsCaller"; - - static constexpr llvm::StringLiteral GlobalCRuntimeUserEntrySelectorName = - "__psrCRuntimeUserEntrySelector"; + GlobalCtorsDtorsModel::ModelName; /// Constructs the ICFG based on the given IRDB and the entry-points using a /// fixpoint iteration. This may take a long time. @@ -97,10 +88,10 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { bool IncludeGlobals = true); /// Creates an ICFG with an already given call-graph - explicit LLVMBasedICFG(CallGraph CG, LLVMProjectIRDB *IRDB); + explicit LLVMBasedICFG(CallGraph CG, const LLVMProjectIRDB *IRDB); - explicit LLVMBasedICFG(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedCG); + explicit LLVMBasedICFG(const LLVMProjectIRDB *IRDB, + const CallGraphData &SerializedCG); // Deleter of LLVMTypeHierarchy may be unknown here... ~LLVMBasedICFG(); @@ -133,14 +124,19 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { } /// Gets the underlying IRDB - [[nodiscard]] LLVMProjectIRDB *getIRDB() const noexcept { return IRDB; } + [[nodiscard]] const LLVMProjectIRDB *getIRDB() const noexcept { return IRDB; } /// Returns true, if a function was generated by phasar. - [[nodiscard]] static bool isPhasarGenerated(const llvm::Function &) noexcept; + [[nodiscard]] static bool + isPhasarGenerated(const llvm::Function &F) noexcept { + return GlobalCtorsDtorsModel::isPhasarGenerated(F); + } using CFGBase::print; using ICFGBase::print; + using ICFGBase::printAsJson; + using CFGBase::getAsJson; using ICFGBase::getAsJson; @@ -155,8 +151,9 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { [[nodiscard]] llvm::SmallVector getReturnSitesOfCallAtImpl(n_t Inst) const; void printImpl(llvm::raw_ostream &OS) const; - [[nodiscard]] nlohmann::json getAsJsonImpl() const; - [[nodiscard]] const CallGraph &getCallGraphImpl() const noexcept { + void printAsJsonImpl(llvm::raw_ostream &OS) const; + [[nodiscard, deprecated]] nlohmann::json getAsJsonImpl() const; + [[nodiscard]] const LLVMBasedCallGraph &getCallGraphImpl() const noexcept { return CG; } @@ -168,14 +165,13 @@ class LLVMBasedICFG : public LLVMBasedCFG, public ICFGBase { llvm::Module &M, llvm::ArrayRef UserEntryPoints); void initialize(LLVMProjectIRDB *IRDB, Resolver &CGResolver, - llvm::ArrayRef EntryPoints, - const LLVMVFTableProvider &VTP, Soundness S, + llvm::ArrayRef EntryPoints, Soundness S, bool IncludeGlobals); // --- - CallGraph CG; - LLVMProjectIRDB *IRDB = nullptr; + LLVMBasedCallGraph CG; + const LLVMProjectIRDB *IRDB = nullptr; LLVMVFTableProvider VTP; }; diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h index 299a600ea..7d4bf8842 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/CHAResolver.h @@ -22,7 +22,6 @@ namespace llvm { class CallBase; -class Function; } // namespace llvm namespace psr { @@ -38,6 +37,11 @@ class CHAResolver : public Resolver { [[nodiscard]] std::string str() const override; + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } + protected: MaybeUniquePtr TH; }; diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h index 7b4484f6e..371b6cfc8 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/DTAResolver.h @@ -37,6 +37,22 @@ class DTAResolver : public CHAResolver { public: using TypeGraph_t = CachedTypeGraph; + DTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, + const LLVMTypeHierarchy *TH); + + ~DTAResolver() override = default; + + FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; + + void otherInst(const llvm::Instruction *Inst) override; + + [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } + protected: TypeGraph_t TypeGraph; @@ -53,18 +69,6 @@ class DTAResolver : public CHAResolver { * of vtable) */ bool heuristicAntiConstructorVtablePos(const llvm::BitCastInst *BitCast); - -public: - DTAResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, - const LLVMTypeHierarchy *TH); - - ~DTAResolver() override = default; - - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; - - void otherInst(const llvm::Instruction *Inst) override; - - [[nodiscard]] std::string str() const override; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h index 7fa845080..376eb5962 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/NOResolver.h @@ -13,39 +13,27 @@ #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" namespace llvm { -class Instruction; class CallBase; -class Function; -class StructType; } // namespace llvm namespace psr { class NOResolver final : public Resolver { -protected: - const llvm::Function * - getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, - const llvm::CallBase *CallSite); - public: - NOResolver(const LLVMProjectIRDB *IRDB); + NOResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP); ~NOResolver() override = default; - void preCall(const llvm::Instruction *Inst) override; - - void handlePossibleTargets(const llvm::CallBase *CallSite, - FunctionSetTy &PossibleTargets) override; - - void postCall(const llvm::Instruction *Inst) override; - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite) override; - void otherInst(const llvm::Instruction *Inst) override; - [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h index e278cf786..ebe8fb660 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/OTFResolver.h @@ -26,7 +26,6 @@ #include namespace llvm { -class Instruction; class CallBase; class Function; class Type; @@ -38,22 +37,15 @@ namespace psr { class LLVMTypeHierarchy; class OTFResolver : public Resolver { -protected: - LLVMAliasInfoRef PT; - public: OTFResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, LLVMAliasInfoRef PT); ~OTFResolver() override = default; - void preCall(const llvm::Instruction *Inst) override; - void handlePossibleTargets(const llvm::CallBase *CallSite, FunctionSetTy &CalleeTargets) override; - void postCall(const llvm::Instruction *Inst) override; - FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) override; FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite) override; @@ -66,6 +58,14 @@ class OTFResolver : public Resolver { const llvm::Function *CalleeTarget); [[nodiscard]] std::string str() const override; + + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return true; + } + +protected: + LLVMAliasInfoRef PT; }; } // namespace psr diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h index 6010443f0..1535fd0f8 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/RTAResolver.h @@ -24,8 +24,6 @@ namespace llvm { class CallBase; class StructType; -class Function; -class StructType; } // namespace llvm namespace psr { @@ -40,6 +38,11 @@ class RTAResolver : public CHAResolver { [[nodiscard]] std::string str() const override; + [[nodiscard]] bool + mutatesHelperAnalysisInformation() const noexcept override { + return false; + } + private: void resolveAllocatedStructTypes(); diff --git a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h index e8f997f17..156543886 100644 --- a/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h +++ b/include/phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h @@ -49,13 +49,14 @@ getReceiverType(const llvm::CallBase *CallSite); [[nodiscard]] bool isConsistentCall(const llvm::CallBase *CallSite, const llvm::Function *DestFun); +[[nodiscard]] bool isVirtualCall(const llvm::Instruction *Inst, + const LLVMVFTableProvider &VTP); + class Resolver { protected: const LLVMProjectIRDB *IRDB; const LLVMVFTableProvider *VTP; - Resolver(const LLVMProjectIRDB *IRDB); - const llvm::Function * getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, const llvm::CallBase *CallSite); @@ -74,14 +75,23 @@ class Resolver { virtual void postCall(const llvm::Instruction *Inst); - virtual FunctionSetTy resolveVirtualCall(const llvm::CallBase *CallSite) = 0; + [[nodiscard]] FunctionSetTy + resolveIndirectCall(const llvm::CallBase *CallSite); + + [[nodiscard]] virtual FunctionSetTy + resolveVirtualCall(const llvm::CallBase *CallSite) = 0; - virtual FunctionSetTy resolveFunctionPointer(const llvm::CallBase *CallSite); + [[nodiscard]] virtual FunctionSetTy + resolveFunctionPointer(const llvm::CallBase *CallSite); virtual void otherInst(const llvm::Instruction *Inst); [[nodiscard]] virtual std::string str() const = 0; + [[nodiscard]] virtual bool mutatesHelperAnalysisInformation() const noexcept { + // Conservatively returns true. Override if possible + return true; + } static std::unique_ptr create(CallGraphAnalysisType Ty, const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, diff --git a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h index a33b6e10d..4f0e943b7 100644 --- a/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h +++ b/include/phasar/PhasarLLVM/Passes/GeneralStatisticsAnalysis.h @@ -176,7 +176,9 @@ struct GeneralStatistics { "instead")]] const std::set & getRetResInstructions() const; - [[nodiscard]] nlohmann::json getAsJson() const; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; }; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h index c266980b1..83c22dbef 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasGraph.h @@ -58,8 +58,9 @@ struct AliasInfoTraits * * @brief Represents the points-to graph of a function. */ -class LLVMAliasGraph : public AnalysisPropertiesMixin, - AliasInfoBaseUtils { +class [[deprecated("Use LLVMAliasSet Instead")]] LLVMAliasGraph + : public AnalysisPropertiesMixin, + AliasInfoBaseUtils { using traits_t = AliasInfoTraits; public: @@ -126,9 +127,9 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, * considered. False, if May and Must Aliases should be * considered. */ - explicit LLVMAliasGraph( - LLVMProjectIRDB &IRDB, bool UseLazyEvaluation = true, - AliasAnalysisType PATy = AliasAnalysisType::CFLAnders); + explicit LLVMAliasGraph(LLVMProjectIRDB & IRDB, bool UseLazyEvaluation = true, + AliasAnalysisType PATy = + AliasAnalysisType::CFLAnders); /** * @brief Returns true if graph contains 0 nodes. @@ -162,15 +163,15 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, * @param F Function pointer * @return Vector with pointers. */ - std::vector - getPointersEscapingThroughReturnsForFunction(const llvm::Function *Fd) const; + std::vector getPointersEscapingThroughReturnsForFunction( + const llvm::Function *Fd) const; /** * @brief Checks if a given value is represented by a vertex in the points-to * graph. * @return True, the points-to graph contains the given value. */ - bool containsValue(llvm::Value *V); + bool containsValue(llvm::Value * V); /** * The value-vertex-map maps each Value of the points-to graph to @@ -192,8 +193,8 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, const graph_t &PAG; }; - static inline PointerVertexOrEdgePrinter - makePointerVertexOrEdgePrinter(const graph_t &PAG) { + static inline PointerVertexOrEdgePrinter makePointerVertexOrEdgePrinter( + const graph_t &PAG) { return {PAG}; } @@ -220,9 +221,9 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, AliasSetPtrTy getAliasSet(const llvm::Value *V, const llvm::Instruction *I = nullptr); - AllocationSiteSetPtrTy - getReachableAllocationSites(const llvm::Value *V, bool IntraProcOnly = false, - const llvm::Instruction *I = nullptr); + AllocationSiteSetPtrTy getReachableAllocationSites( + const llvm::Value *V, bool IntraProcOnly = false, + const llvm::Instruction *I = nullptr); [[nodiscard]] bool isInReachableAllocationSites( const llvm::Value *V, const llvm::Value *PotentialValue, @@ -251,7 +252,7 @@ class LLVMAliasGraph : public AnalysisPropertiesMixin, void computeAliasGraph(const llvm::Value *V); - void computeAliasGraph(llvm::Function *F); + void computeAliasGraph(llvm::Function * F); struct AllocationSiteDFSVisitor; struct ReachabilityDFSVisitor; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h index 5fc858204..92644c828 100644 --- a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSet.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H #define PHASAR_PHASARLLVM_POINTER_LLVMALIASSET_H +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" #include "phasar/PhasarLLVM/Pointer/LLVMBasedAliasAnalysis.h" #include "phasar/Pointer/AliasInfoBase.h" #include "phasar/Pointer/AliasInfoTraits.h" @@ -97,7 +98,11 @@ class LLVMAliasSet : public AnalysisPropertiesMixin, void print(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] nlohmann::json getAsJson() const; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const; + + [[nodiscard]] LLVMAliasSetData getLLVMAliasSetData() const; void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; diff --git a/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h new file mode 100644 index 000000000..bf924e39c --- /dev/null +++ b/include/phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMALIASSETDATA_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMALIASSETDATA_H + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMAliasSetData { + std::vector> AliasSets; + std::vector AnalyzedFunctions; + + LLVMAliasSetData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static LLVMAliasSetData deserializeJson(const llvm::Twine &Path); + static LLVMAliasSetData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMALIASSETDATA_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h index dc7df6ea4..0ea35bbdb 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H #define PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHY_H +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/TypeHierarchy/TypeHierarchy.h" @@ -19,6 +20,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" +#include "llvm/Support/Casting.h" #include @@ -32,6 +34,8 @@ class DIBasedTypeHierarchy using f_t = const llvm::Function *; explicit DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB); + explicit DIBasedTypeHierarchy(const LLVMProjectIRDB *IRDB, + const DIBasedTypeHierarchyData &SerializedData); ~DIBasedTypeHierarchy() override = default; [[nodiscard]] bool hasType(ClassType Type) const override { @@ -64,6 +68,10 @@ class DIBasedTypeHierarchy [[nodiscard]] const auto &getAllVTables() const noexcept { return VTables; } [[nodiscard]] llvm::StringRef getTypeName(ClassType Type) const override { + if (const auto *CompTy = llvm::dyn_cast(Type)) { + auto Ident = CompTy->getIdentifier(); + return Ident.empty() ? CompTy->getName() : Ident; + } return Type->getName(); } @@ -82,9 +90,18 @@ class DIBasedTypeHierarchy */ void printAsDot(llvm::raw_ostream &OS = llvm::outs()) const; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; + + /** + * @brief Prints the class hierarchy to an ostream in json format. + * @param an outputstream + */ + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const override; private: + [[nodiscard]] DIBasedTypeHierarchyData getTypeHierarchyData() const; [[nodiscard]] llvm::iterator_range subTypesOf(size_t TypeIdx) const noexcept; diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h new file mode 100644 index 000000000..52cd85569 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +#include +#include +#include + +namespace psr { +struct DIBasedTypeHierarchyData { + // DITypes and llvm::Function * are serialized by serializing their names and + // using the DebugInfoFinder to deserialize them + + std::vector VertexTypes; + std::vector> TransitiveDerivedIndex; + std::vector Hierarchy; + std::vector> VTables; + + DIBasedTypeHierarchyData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static DIBasedTypeHierarchyData deserializeJson(const llvm::Twine &Path); + static DIBasedTypeHierarchyData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_DIBASEDTYPEHIERARCHYDATA_H diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h index 98c4dde0b..3678dfa44 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h @@ -17,10 +17,12 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHY_H_ #define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHY_H_ +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/TypeHierarchy/TypeHierarchy.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/raw_ostream.h" #include "boost/graph/adjacency_list.hpp" #include "boost/graph/graph_traits.hpp" @@ -134,6 +136,8 @@ class LLVMTypeHierarchy * @param IRDB ProjectIRCompiledDB object. */ LLVMTypeHierarchy(const LLVMProjectIRDB &IRDB); + LLVMTypeHierarchy(const LLVMProjectIRDB &IRDB, + const LLVMTypeHierarchyData &SerializedData); /** * @brief Creates a LLVMStructTypeHierarchy based on the @@ -187,7 +191,9 @@ class LLVMTypeHierarchy void print(llvm::raw_ostream &OS = llvm::outs()) const override; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; // void mergeWith(LLVMTypeHierarchy &Other); @@ -206,7 +212,7 @@ class LLVMTypeHierarchy * @brief Prints the class hierarchy to an ostream in json format. * @param an outputstream */ - void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const; + void printAsJson(llvm::raw_ostream &OS = llvm::outs()) const override; // void printGraphAsDot(llvm::raw_ostream &out); diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h new file mode 100644 index 000000000..b9757d0b3 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.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: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ + +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMTypeHierarchyData { + std::string PhasarConfigJsonTypeHierarchyID; + // key = vertex, value = edges + llvm::StringMap> TypeGraph; + + LLVMTypeHierarchyData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS); + + static LLVMTypeHierarchyData deserializeJson(const llvm::Twine &Path); + static LLVMTypeHierarchyData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMTYPEHIERARCHYDATA_H_ diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h index fd55a006a..20bfb5196 100644 --- a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h @@ -10,6 +10,7 @@ #ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLE_H_ #define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLE_H_ +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" #include "phasar/TypeHierarchy/VFTable.h" #include "nlohmann/json.hpp" @@ -36,6 +37,9 @@ class LLVMVFTable : public VFTable { std::vector VFT; public: + // NOLINTNEXTLINE + static constexpr char NullFunName[] = "__null__"; + LLVMVFTable() = default; LLVMVFTable(std::vector Fs) : VFT(std::move(Fs)) {} ~LLVMVFTable() override = default; @@ -66,7 +70,13 @@ class LLVMVFTable : public VFTable { void print(llvm::raw_ostream &OS) const override; - [[nodiscard]] nlohmann::json getAsJson() const override; + [[nodiscard]] [[deprecated( + "Please use printAsJson() instead")]] nlohmann::json + getAsJson() const override; + + [[nodiscard]] LLVMVFTableData getVFTableData() const; + + void printAsJson(llvm::raw_ostream &OS) const override; [[nodiscard]] std::vector::iterator begin() { return VFT.begin(); diff --git a/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h new file mode 100644 index 000000000..e3997d9f5 --- /dev/null +++ b/include/phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h @@ -0,0 +1,30 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#ifndef PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ +#define PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ + +#include "llvm/Support/raw_ostream.h" + +#include + +namespace psr { +struct LLVMVFTableData { + std::vector VFT; + + LLVMVFTableData() noexcept = default; + void printAsJson(llvm::raw_ostream &OS) const; + + static LLVMVFTableData deserializeJson(const llvm::Twine &Path); + static LLVMVFTableData loadJsonString(llvm::StringRef JsonAsString); +}; + +} // namespace psr + +#endif // PHASAR_PHASARLLVM_TYPEHIERARCHY_LLVMVFTABLEDATA_H_ diff --git a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h index 46f1b05a9..b0d8bccf9 100644 --- a/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h +++ b/include/phasar/PhasarLLVM/Utils/LLVMShorthands.h @@ -19,8 +19,6 @@ #include "phasar/Utils/Utilities.h" -#include "llvm/ADT/DenseMap.h" - #include #include @@ -249,7 +247,7 @@ llvm::StringRef getVarAnnotationIntrinsicName(const llvm::CallInst *CallInst); class ModulesToSlotTracker { friend class LLVMProjectIRDB; - friend class LLVMBasedICFG; + friend class GlobalCtorsDtorsModel; friend class LLVMZeroValue; private: diff --git a/include/phasar/Pointer/AliasInfo.h b/include/phasar/Pointer/AliasInfo.h index ba9fa480f..3ab63412e 100644 --- a/include/phasar/Pointer/AliasInfo.h +++ b/include/phasar/Pointer/AliasInfo.h @@ -21,8 +21,6 @@ #include "llvm/Support/TypeName.h" #include "llvm/Support/raw_ostream.h" -#include "nlohmann/json.hpp" - #include #include @@ -140,7 +138,8 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { VT->Print(AA, OS); } - [[nodiscard]] nlohmann::json getAsJson() const { + [[nodiscard, deprecated("Use printAsJson() instead")]] nlohmann::json + getAsJson() const { assert(VT != nullptr); return VT->GetAsJson(AA); } @@ -240,8 +239,14 @@ class AliasInfoRef : public AnalysisPropertiesMixin> { [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->print(OS); }, - [](const void *AA) { - return static_cast(AA)->getAsJson(); + [](const void *AA) noexcept { +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdeprecated" + if constexpr (has_getAsJson::value) { + return static_cast(AA)->getAsJson(); + } + return nlohmann::json(); +#pragma GCC diagnostic pop }, [](const void *AA, llvm::raw_ostream &OS) { static_cast(AA)->printAsJson(OS); diff --git a/include/phasar/Pointer/AliasInfoBase.h b/include/phasar/Pointer/AliasInfoBase.h index 039a212ed..8e57f200c 100644 --- a/include/phasar/Pointer/AliasInfoBase.h +++ b/include/phasar/Pointer/AliasInfoBase.h @@ -49,7 +49,7 @@ auto testAliasInfo( CAI.isInterProcedural(), CAI.getAliasAnalysisType(), AI.alias(*VT, *VT, *NT), AI.getAliasSet(*VT, *NT), AI.getReachableAllocationSites(*VT, true, *NT), - AI.isInReachableAllocationSites(*VT, *VT, true, *NT), CAI.getAsJson(), + AI.isInReachableAllocationSites(*VT, *VT, true, *NT), CAI.getAnalysisProperties(), CAI.isContextSensitive(), CAI.isFieldSensitive(), CAI.isFlowSensitive())); template @@ -69,7 +69,7 @@ struct IsAliasInfo< std::tuple::AliasSetPtrTy, typename AliasInfoTraits::AllocationSiteSetPtrTy, bool, - nlohmann::json, AnalysisProperties, bool, bool, bool>, + AnalysisProperties, bool, bool, bool>, decltype(testAliasInfo(std::declval(), std::declval()))>>> : std::true_type { }; diff --git a/include/phasar/TypeHierarchy/TypeHierarchy.h b/include/phasar/TypeHierarchy/TypeHierarchy.h index 842c46633..d753652cf 100644 --- a/include/phasar/TypeHierarchy/TypeHierarchy.h +++ b/include/phasar/TypeHierarchy/TypeHierarchy.h @@ -39,7 +39,12 @@ template class TypeHierarchy { [[nodiscard]] virtual bool empty() const noexcept = 0; virtual void print(llvm::raw_ostream &OS = llvm::outs()) const = 0; - [[nodiscard]] virtual nlohmann::json getAsJson() const = 0; + + [[nodiscard, + deprecated("Please use printAsJson() instead")]] virtual nlohmann::json + getAsJson() const = 0; + + virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; template diff --git a/include/phasar/TypeHierarchy/VFTable.h b/include/phasar/TypeHierarchy/VFTable.h index 56334171b..83c544dbc 100644 --- a/include/phasar/TypeHierarchy/VFTable.h +++ b/include/phasar/TypeHierarchy/VFTable.h @@ -38,7 +38,11 @@ template class VFTable { virtual void print(llvm::raw_ostream &OS) const = 0; - [[nodiscard]] virtual nlohmann::json getAsJson() const = 0; + [[nodiscard, + deprecated("Please use printAsJson() instead")]] virtual nlohmann::json + getAsJson() const = 0; + + virtual void printAsJson(llvm::raw_ostream &OS) const = 0; }; template diff --git a/include/phasar/Utils/TypeTraits.h b/include/phasar/Utils/TypeTraits.h index 265d73ee5..9a80759b8 100644 --- a/include/phasar/Utils/TypeTraits.h +++ b/include/phasar/Utils/TypeTraits.h @@ -13,6 +13,8 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" +#include "nlohmann/json.hpp" + #include #include #include @@ -273,6 +275,12 @@ template using type_identity_t = typename type_identity::type; template static constexpr size_t variant_idx = detail::variant_idx::value; +template +struct has_getAsJson : std::false_type {}; // NOLINT +template +struct has_getAsJson().getAsJson())> + : std::true_type {}; // NOLINT + struct TrueFn { template [[nodiscard]] bool operator()(const Args &.../*unused*/) const noexcept { diff --git a/lib/ControlFlow/CMakeLists.txt b/lib/ControlFlow/CMakeLists.txt index 39084cd11..d6f91baa6 100644 --- a/lib/ControlFlow/CMakeLists.txt +++ b/lib/ControlFlow/CMakeLists.txt @@ -2,6 +2,8 @@ file(GLOB_RECURSE CONTROLFLOW_SRC *.h *.cpp) add_phasar_library(phasar_controlflow ${CONTROLFLOW_SRC} + LINKS + phasar_utils LLVM_LINK_COMPONENTS Support LINK_PRIVATE nlohmann_json::nlohmann_json ) diff --git a/lib/ControlFlow/CallGraphData.cpp b/lib/ControlFlow/CallGraphData.cpp new file mode 100644 index 000000000..5b4e254f6 --- /dev/null +++ b/lib/ControlFlow/CallGraphData.cpp @@ -0,0 +1,59 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#include "phasar/ControlFlow/CallGraphData.h" + +#include "phasar/Utils/IO.h" +#include "phasar/Utils/Logger.h" +#include "phasar/Utils/NlohmannLogging.h" + +namespace psr { +static CallGraphData getDataFromJson(const nlohmann::json &Json) { + CallGraphData ToReturn; + + // map F to vector of n_t's + for (const auto &[FVal, FunctionVertexTyVals] : + Json.get()) { + auto &FValMappedVector = ToReturn.FToFunctionVertexTy[FVal]; + if (FunctionVertexTyVals.is_array()) { + FValMappedVector = FunctionVertexTyVals.get>(); + } else if (!FunctionVertexTyVals.is_null()) { + PHASAR_LOG_LEVEL( + WARNING, "Invalid Function-CS IDs Array: " << FunctionVertexTyVals); + } + } + + return ToReturn; +} + +void CallGraphData::printAsJson(llvm::raw_ostream &OS) { + nlohmann::json JSON; + + for (const auto &[Fun, Callers] : FToFunctionVertexTy) { + auto &JCallers = JSON[Fun]; + + for (const auto &CS : Callers) { + JCallers.push_back(CS); + } + } + + OS << JSON << '\n'; +} + +CallGraphData CallGraphData::deserializeJson(const llvm::Twine &Path) { + return getDataFromJson(readJsonFile(Path)); +} + +CallGraphData CallGraphData::loadJsonString(llvm::StringRef JsonAsString) { + auto ToStringify = + nlohmann::json::parse(JsonAsString.begin(), JsonAsString.end()); + return getDataFromJson(ToStringify); +} + +} // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/EntryFunctionUtils.cpp b/lib/PhasarLLVM/ControlFlow/EntryFunctionUtils.cpp new file mode 100644 index 000000000..06986f9c2 --- /dev/null +++ b/lib/PhasarLLVM/ControlFlow/EntryFunctionUtils.cpp @@ -0,0 +1,68 @@ +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" + +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/Utils/Logger.h" + +std::vector +psr::getEntryFunctions(const LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints) { + std::vector UserEntryPointFns; + if (EntryPoints.size() == 1 && EntryPoints.front() == "__ALL__") { + UserEntryPointFns.reserve(IRDB.getNumFunctions()); + // Handle the special case in which a user wishes to treat all functions as + // entry points. + for (const auto *Fun : IRDB.getAllFunctions()) { + // Only functions with external linkage (or 'main') can be called from the + // outside! + if (!Fun->isDeclaration() && Fun->hasName() && + (Fun->hasExternalLinkage() || Fun->getName() == "main")) { + UserEntryPointFns.push_back(Fun); + } + } + } else { + UserEntryPointFns.reserve(EntryPoints.size()); + for (const auto &EntryPoint : EntryPoints) { + const auto *F = IRDB.getFunctionDefinition(EntryPoint); + if (F == nullptr) { + PHASAR_LOG_LEVEL(WARNING, + "Could not retrieve function for entry point '" + << EntryPoint << "'"); + continue; + } + UserEntryPointFns.push_back(F); + } + } + return UserEntryPointFns; +} + +[[nodiscard]] std::vector +psr::getEntryFunctionsMut(LLVMProjectIRDB &IRDB, + llvm::ArrayRef EntryPoints) { + std::vector UserEntryPointFns; + if (EntryPoints.size() == 1 && EntryPoints.front() == "__ALL__") { + UserEntryPointFns.reserve(IRDB.getNumFunctions()); + // Handle the special case in which a user wishes to treat all functions as + // entry points. + for (const auto *Fun : IRDB.getAllFunctions()) { + // Only functions with external linkage (or 'main') can be called from the + // outside! + if (!Fun->isDeclaration() && Fun->hasName() && + (Fun->hasExternalLinkage() || Fun->getName() == "main")) { + UserEntryPointFns.push_back(IRDB.getFunction(Fun->getName())); + } + } + } else { + UserEntryPointFns.reserve(EntryPoints.size()); + for (const auto &EntryPoint : EntryPoints) { + auto *F = IRDB.getFunctionDefinition(EntryPoint); + if (F == nullptr) { + PHASAR_LOG_LEVEL(WARNING, + "Could not retrieve function for entry point '" + << EntryPoint << "'"); + continue; + } + UserEntryPointFns.push_back(F); + } + } + return UserEntryPointFns; +} diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobalsImpl.cpp b/lib/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.cpp similarity index 86% rename from lib/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobalsImpl.cpp rename to lib/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.cpp index eeb0b2393..e6a53249b 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobalsImpl.cpp +++ b/lib/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.cpp @@ -7,7 +7,9 @@ * Philipp Schubert, Fabian Schiebel and others *****************************************************************************/ -#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" +#include "phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h" + +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" #include "phasar/Utils/Logger.h" @@ -124,10 +126,10 @@ static llvm::Function *createDtorCallerForModule( &RegisteredDtors) { auto *PhasarDtorCaller = llvm::cast( - Mod.getOrInsertFunction( - LLVMBasedICFG::GlobalCRuntimeDtorsCallerName.str() + '.' + - getReducedModuleName(Mod), - llvm::Type::getVoidTy(Mod.getContext())) + Mod.getOrInsertFunction((GlobalCtorsDtorsModel::DtorsCallerName + + llvm::Twine('.') + getReducedModuleName(Mod)) + .str(), + llvm::Type::getVoidTy(Mod.getContext())) .getCallee()); auto *BB = @@ -196,7 +198,7 @@ static std::pair buildCRuntimeGlobalDtorsModel( auto &CTX = M.getContext(); auto *Cleanup = llvm::cast( - M.getOrInsertFunction(LLVMBasedICFG::GlobalCRuntimeDtorModelName, + M.getOrInsertFunction(GlobalCtorsDtorsModel::DtorModelName, llvm::Type::getVoidTy(CTX)) .getCallee()); @@ -217,24 +219,25 @@ static std::pair buildCRuntimeGlobalDtorsModel( return {Cleanup, true}; } -llvm::Function *LLVMBasedICFG::buildCRuntimeGlobalCtorsDtorsModel( - llvm::Module &M, llvm::ArrayRef UserEntryPoints) { +llvm::Function *GlobalCtorsDtorsModel::buildModel( + LLVMProjectIRDB &IRDB, llvm::ArrayRef UserEntryPoints) { + auto &M = *IRDB.getModule(); auto GlobalCtors = collectGlobalCtors(M); auto GlobalDtors = collectGlobalDtors(M); auto *RegisteredDtorCaller = collectRegisteredDtors(GlobalDtors, M); if (RegisteredDtorCaller) { - IRDB->insertFunction(RegisteredDtorCaller); + IRDB.insertFunction(RegisteredDtorCaller); } auto [GlobalCleanupFn, Inserted] = buildCRuntimeGlobalDtorsModel(M, GlobalDtors); if (Inserted) { - IRDB->insertFunction(GlobalCleanupFn); + IRDB.insertFunction(GlobalCleanupFn); } auto &CTX = M.getContext(); auto *GlobModel = llvm::cast( - M.getOrInsertFunction(GlobalCRuntimeModelName, + M.getOrInsertFunction(ModelName, /*retTy*/ llvm::Type::getVoidTy(CTX), /*argc*/ @@ -301,8 +304,8 @@ llvm::Function *LLVMBasedICFG::buildCRuntimeGlobalCtorsDtorsModel( IRB.CreateRetVoid(); } else { - auto UEntrySelectorFn = M.getOrInsertFunction( - GlobalCRuntimeUserEntrySelectorName, llvm::Type::getInt32Ty(CTX)); + auto UEntrySelectorFn = M.getOrInsertFunction(UserEntrySelectorName, + llvm::Type::getInt32Ty(CTX)); auto *UEntrySelector = IRB.CreateCall(UEntrySelectorFn); @@ -335,9 +338,30 @@ llvm::Function *LLVMBasedICFG::buildCRuntimeGlobalCtorsDtorsModel( IRB.CreateRetVoid(); } - IRDB->insertFunction(GlobModel); + IRDB.insertFunction(GlobModel); ModulesToSlotTracker::updateMSTForModule(&M); return GlobModel; } + +llvm::Function * +GlobalCtorsDtorsModel::buildModel(LLVMProjectIRDB &IRDB, + llvm::ArrayRef UserEntryPoints) { + auto UserEntryPointFns = getEntryFunctionsMut(IRDB, UserEntryPoints); + return buildModel(IRDB, UserEntryPointFns); +} + +bool GlobalCtorsDtorsModel::isPhasarGenerated( + const llvm::Function &F) noexcept { + if (F.hasName()) { + llvm::StringRef FunctionName = F.getName(); + return llvm::StringSwitch(FunctionName) + .Cases(ModelName, DtorModelName, DtorsCallerName, UserEntrySelectorName, + true) + .Default(false); + } + + return false; +} + } // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp index 39aee09cc..53a3a3adb 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedBackwardICFG.cpp @@ -64,6 +64,10 @@ void LLVMBasedBackwardICFG::printImpl(llvm::raw_ostream &OS) const { ForwardICFG->print(OS); } +void LLVMBasedBackwardICFG::printAsJsonImpl(llvm::raw_ostream &OS) const { + ForwardICFG->printAsJson(OS); +} + nlohmann::json LLVMBasedBackwardICFG::getAsJsonImpl() const { return ForwardICFG->getAsJson(); } diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp new file mode 100644 index 000000000..74e8a9716 --- /dev/null +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.cpp @@ -0,0 +1,306 @@ +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h" + +#include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/PhasarLLVM/ControlFlow/EntryFunctionUtils.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSet.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/PAMMMacros.h" +#include "phasar/Utils/Soundness.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" + +#include + +namespace { +using namespace psr; +struct Builder { + const LLVMProjectIRDB *IRDB = nullptr; + Resolver *Res = nullptr; + CallGraphBuilder + CGBuilder{}; + llvm::DenseSet VisitedFunctions{}; + + llvm::Function *GlobalCleanupFn = nullptr; + + llvm::SmallDenseMap + GlobalRegisteredDtorsCaller{}; + + // The worklist for direct callee resolution. + llvm::SmallVector FunctionWL{}; + + // Map indirect calls to the number of possible targets found for it. Fixpoint + // is not reached when more targets are found. + llvm::DenseMap IndirectCalls{}; + + void initWorkList(llvm::ArrayRef EntryPointFns); + + [[nodiscard]] CallGraph + buildCallGraph(Soundness S); + + /// \returns FixPointReached + bool processFunction(/*bidigraph_t &Callgraph,*/ const llvm::Function *F); + /// \returns FoundNewTargets + bool constructDynamicCall(const llvm::Instruction *CS); +}; + +void Builder::initWorkList( + llvm::ArrayRef EntryPointFns) { + FunctionWL.reserve(IRDB->getNumFunctions()); + FunctionWL.append(EntryPointFns.begin(), EntryPointFns.end()); + + CGBuilder.reserve(IRDB->getNumFunctions()); +} + +auto Builder::buildCallGraph(Soundness S) -> LLVMBasedCallGraph { + PHASAR_LOG_LEVEL_CAT(INFO, "LLVMBasedICFG", + "Starting CallGraphAnalysisType: " << Res->str()); + VisitedFunctions.reserve(IRDB->getNumFunctions()); + + bool RequiresIndirectCallsFixpoint = + S != psr::Soundness::Unsound && Res->mutatesHelperAnalysisInformation(); + + bool FixpointReached; + + do { + FixpointReached = true; + while (!FunctionWL.empty()) { + const llvm::Function *F = FunctionWL.pop_back_val(); + FixpointReached &= processFunction(F); + } + + if (RequiresIndirectCallsFixpoint) { + /// XXX This can probably be done more efficiently. + /// However, we cannot just work on the IndirectCalls-delta as we are + /// mutating the points-to-info on the fly + for (auto [CS, _] : IndirectCalls) { + FixpointReached &= !constructDynamicCall(CS); + } + } + } while (!FixpointReached); + for (const auto &[IndirectCall, Targets] : IndirectCalls) { + if (Targets == 0) { + PHASAR_LOG_LEVEL(WARNING, "No callees found for callsite " + << llvmIRToString(IndirectCall)); + } + } + + PAMM_GET_INSTANCE; + REG_COUNTER("CG Functions", CGBuilder.viewCallGraph().getNumVertexFunctions(), + Full); + REG_COUNTER("CG CallSites", CGBuilder.viewCallGraph().getNumVertexCallSites(), + Full); + PHASAR_LOG_LEVEL_CAT(INFO, "LLVMBasedICFG", + "Call graph has been constructed"); + return CGBuilder.consumeCallGraph(); +} + +static bool fillPossibleTargets( + Resolver::FunctionSetTy &PossibleTargets, Resolver &Res, + const llvm::CallBase *CS, + llvm::DenseMap &IndirectCalls) { + if (const auto *StaticCallee = CS->getCalledFunction()) { + PossibleTargets.insert(StaticCallee); + + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Found static call-site: " + << " " << llvmIRToString(CS)); + return true; + } + + // still try to resolve the called function statically + const llvm::Value *SV = CS->getCalledOperand()->stripPointerCastsAndAliases(); + if (const auto *ValueFunction = llvm::dyn_cast(SV)) { + PossibleTargets.insert(ValueFunction); + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Found static call-site: " << llvmIRToString(CS)); + return true; + } + + if (llvm::isa(SV)) { + return true; + } + + // the function call must be resolved dynamically + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Found dynamic call-site: " + << " " << llvmIRToString(CS)); + + PossibleTargets = Res.resolveIndirectCall(CS); + + IndirectCalls[CS] = PossibleTargets.size(); + return false; +} + +bool Builder::processFunction(const llvm::Function *F) { + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Walking in function: " << F->getName()); + if (F->isDeclaration() || !VisitedFunctions.insert(F).second) { + PHASAR_LOG_LEVEL_CAT( + DEBUG, "LLVMBasedICFG", + "Function already visited or only declaration: " << F->getName()); + return true; + } + + assert(Res != nullptr); + + // add a node for function F to the call graph (if not present already) + std::ignore = CGBuilder.addFunctionVertex(F); + + bool FixpointReached = true; + + // iterate all instructions of the current function + Resolver::FunctionSetTy PossibleTargets; + for (const auto &I : llvm::instructions(F)) { + const auto *CS = llvm::dyn_cast(&I); + if (!CS) { + Res->otherInst(&I); + continue; + } + + Res->preCall(&I); + scope_exit PostCall = [&] { Res->postCall(&I); }; + + FixpointReached &= + fillPossibleTargets(PossibleTargets, *Res, CS, IndirectCalls); + + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Found " << PossibleTargets.size() + << " possible target(s)"); + + Res->handlePossibleTargets(CS, PossibleTargets); + + auto *CallSiteId = CGBuilder.addInstructionVertex(CS); + + // Insert possible target inside the graph and add the link with + // the current function + for (const auto *PossibleTarget : PossibleTargets) { + CGBuilder.addCallEdge(CS, CallSiteId, PossibleTarget); + FunctionWL.push_back(PossibleTarget); + } + PossibleTargets.clear(); + } + + return FixpointReached; +} + +bool Builder::constructDynamicCall(const llvm::Instruction *CS) { + const auto *CallSite = llvm::dyn_cast(CS); + if (!CallSite) { + llvm::report_fatal_error("[constructDynamicCall]: No call: " + + llvm::Twine(llvmIRToString(CS))); + } + + // Find vertex of callsite. + auto *Callees = CGBuilder.getInstVertexOrNull(CS); + if (!Callees) { + llvm::report_fatal_error( + "[constructDynamicCall]: Did not find vertex of callsite " + + llvm::Twine(llvmIRToString(CS))); + } + + // the function call must be resolved dynamically + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Looking into dynamic call-site: "); + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", " " << llvmIRToString(CS)); + + Res->preCall(CallSite); + scope_exit PostCall = [&] { Res->postCall(CallSite); }; + + // call the resolve routine + + auto PossibleTargets = Res->resolveIndirectCall(CallSite); + + assert(IndirectCalls.count(CallSite)); + auto &NumIndCalls = IndirectCalls[CallSite]; + + if (NumIndCalls >= PossibleTargets.size()) { + // No new targets found + return false; + } + + PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", + "Found " << PossibleTargets.size() - NumIndCalls + << " new possible target(s)"); + NumIndCalls = PossibleTargets.size(); + + // Throw out already found targets + for (const auto *Tgt : *Callees) { + PossibleTargets.erase(Tgt); + } + + Res->handlePossibleTargets(CallSite, PossibleTargets); + // Insert possible target inside the graph and add the link with + // the current function + for (const auto *PossibleTarget : PossibleTargets) { + CGBuilder.addCallEdge(CallSite, Callees, PossibleTarget); + FunctionWL.push_back(PossibleTarget); + } + + return true; +} +} // namespace + +auto psr::buildLLVMBasedCallGraph( + const LLVMProjectIRDB &IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, Soundness S) + -> LLVMBasedCallGraph { + Builder B{&IRDB, &CGResolver}; + + B.initWorkList(EntryPoints); + + PHASAR_LOG_LEVEL_CAT( + INFO, "LLVMBasedICFG", + "Starting ICFG construction " + << std::chrono::steady_clock::now().time_since_epoch().count()); + + scope_exit FinishTiming = [] { + PHASAR_LOG_LEVEL_CAT( + INFO, "LLVMBasedICFG", + "Finished ICFG construction " + << std::chrono::steady_clock::now().time_since_epoch().count()); + }; + + return B.buildCallGraph(S); +} + +auto psr::buildLLVMBasedCallGraph( + LLVMProjectIRDB &IRDB, CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, LLVMTypeHierarchy &TH, + LLVMVFTableProvider &VTP, LLVMAliasInfoRef PT, Soundness S) + -> LLVMBasedCallGraph { + + LLVMAliasInfo PTOwn; + if (!PT && CGType == CallGraphAnalysisType::OTF) { + PTOwn = std::make_unique(&IRDB); + PT = PTOwn.asRef(); + } + + auto Res = Resolver::create(CGType, &IRDB, &VTP, &TH); + return buildLLVMBasedCallGraph(IRDB, *Res, EntryPoints, S); +} + +auto psr::buildLLVMBasedCallGraph(LLVMProjectIRDB &IRDB, + CallGraphAnalysisType CGType, + llvm::ArrayRef EntryPoints, + LLVMTypeHierarchy &TH, + LLVMVFTableProvider &VTP, LLVMAliasInfoRef PT, + Soundness S) -> LLVMBasedCallGraph { + auto EntryPointFns = getEntryFunctions(IRDB, EntryPoints); + return buildLLVMBasedCallGraph(IRDB, CGType, EntryPointFns, TH, VTP, PT, S); +} + +auto psr::buildLLVMBasedCallGraph(const LLVMProjectIRDB &IRDB, + Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S) -> LLVMBasedCallGraph { + auto EntryPointFns = getEntryFunctions(IRDB, EntryPoints); + return buildLLVMBasedCallGraph(IRDB, CGResolver, EntryPointFns, S); +} diff --git a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp b/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp index f8e56c182..2605397da 100644 --- a/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp +++ b/lib/PhasarLLVM/ControlFlow/LLVMBasedICFG.cpp @@ -11,7 +11,10 @@ #include "phasar/ControlFlow/CallGraph.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraph.h" +#include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCallGraphBuilder.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMVFTableProvider.h" #include "phasar/PhasarLLVM/ControlFlow/Resolver/Resolver.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -20,13 +23,9 @@ #include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" #include "phasar/PhasarLLVM/Utils/LLVMBasedContainerConfig.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" -#include "phasar/Utils/Logger.h" -#include "phasar/Utils/MaybeUniquePtr.h" -#include "phasar/Utils/PAMMMacros.h" #include "phasar/Utils/Soundness.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringSwitch.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instruction.h" #include "llvm/Support/ErrorHandling.h" @@ -34,305 +33,16 @@ #include namespace psr { -struct LLVMBasedICFG::Builder { - LLVMProjectIRDB *IRDB = nullptr; - const LLVMVFTableProvider *VTP{}; - Resolver *Res = nullptr; - CallGraphBuilder - CGBuilder{}; - llvm::DenseSet VisitedFunctions{}; - llvm::SmallVector UserEntryPoints{}; - llvm::Function *GlobalCleanupFn = nullptr; - - llvm::SmallDenseMap - GlobalRegisteredDtorsCaller{}; - - // The worklist for direct callee resolution. - llvm::SmallVector FunctionWL{}; - - // Map indirect calls to the number of possible targets found for it. Fixpoint - // is not reached when more targets are found. - llvm::DenseMap IndirectCalls{}; - - void initEntryPoints(llvm::ArrayRef EntryPoints); - void initGlobalsAndWorkList(LLVMBasedICFG *ICFG, bool IncludeGlobals); - [[nodiscard]] CallGraph - buildCallGraph(Soundness S); - - /// \returns FixPointReached - bool processFunction(/*bidigraph_t &Callgraph,*/ const llvm::Function *F); - /// \returns FoundNewTargets - bool constructDynamicCall(const llvm::Instruction *CS); -}; - -void LLVMBasedICFG::Builder::initEntryPoints( - llvm::ArrayRef EntryPoints) { - if (EntryPoints.size() == 1 && EntryPoints.front() == "__ALL__") { - UserEntryPoints.reserve(IRDB->getNumFunctions()); - // Handle the special case in which a user wishes to treat all functions as - // entry points. - for (const auto *Fun : IRDB->getAllFunctions()) { - // Only functions with external linkage (or 'main') can be called from the - // outside! - if (!Fun->isDeclaration() && Fun->hasName() && - (Fun->hasExternalLinkage() || Fun->getName() == "main")) { - UserEntryPoints.push_back(IRDB->getFunction(Fun->getName())); - } - } - } else { - UserEntryPoints.reserve(EntryPoints.size()); - for (const auto &EntryPoint : EntryPoints) { - auto *F = IRDB->getFunctionDefinition(EntryPoint); - if (F == nullptr) { - PHASAR_LOG_LEVEL(WARNING, - "Could not retrieve function for entry point '" - << EntryPoint << "'"); - continue; - } - UserEntryPoints.push_back(F); - } - } -} - -void LLVMBasedICFG::Builder::initGlobalsAndWorkList(LLVMBasedICFG *ICFG, - bool IncludeGlobals) { - FunctionWL.reserve(IRDB->getNumFunctions()); +void LLVMBasedICFG::initialize(LLVMProjectIRDB *IRDB, Resolver &CGResolver, + llvm::ArrayRef EntryPoints, + Soundness S, bool IncludeGlobals) { if (IncludeGlobals) { - const auto *GlobCtor = ICFG->buildCRuntimeGlobalCtorsDtorsModel( - *IRDB->getModule(), UserEntryPoints); - FunctionWL.push_back(GlobCtor); - } else { - FunctionWL.insert(FunctionWL.end(), UserEntryPoints.begin(), - UserEntryPoints.end()); - } - // Note: Pre-allocate the call-graph builder *after* adding the - // CRuntimeGlobalCtorsDtorsModel - CGBuilder.reserve(IRDB->getNumFunctions()); -} - -auto LLVMBasedICFG::Builder::buildCallGraph(Soundness /*S*/) - -> CallGraph { - PHASAR_LOG_LEVEL_CAT(INFO, "LLVMBasedICFG", - "Starting CallGraphAnalysisType: " << Res->str()); - VisitedFunctions.reserve(IRDB->getNumFunctions()); - - bool FixpointReached; - - do { - FixpointReached = true; - while (!FunctionWL.empty()) { - const llvm::Function *F = FunctionWL.pop_back_val(); - FixpointReached &= processFunction(F); - } - - /// XXX This can probably be done more efficiently. - /// However, we cannot just work on the IndirectCalls-delta as we are - /// mutating the points-to-info on the fly - for (auto [CS, _] : IndirectCalls) { - FixpointReached &= !constructDynamicCall(CS); - } - - } while (!FixpointReached); - for (const auto &[IndirectCall, Targets] : IndirectCalls) { - if (Targets == 0) { - PHASAR_LOG_LEVEL(WARNING, "No callees found for callsite " - << llvmIRToString(IndirectCall)); - } - } - - PAMM_GET_INSTANCE; - REG_COUNTER("CG Functions", CGBuilder.viewCallGraph().getNumVertexFunctions(), - Full); - REG_COUNTER("CG CallSites", CGBuilder.viewCallGraph().getNumVertexCallSites(), - Full); - PHASAR_LOG_LEVEL_CAT(INFO, "LLVMBasedICFG", - "Call graph has been constructed"); - return CGBuilder.consumeCallGraph(); -} - -bool LLVMBasedICFG::Builder::processFunction(const llvm::Function *F) { - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Walking in function: " << F->getName()); - if (F->isDeclaration() || !VisitedFunctions.insert(F).second) { - PHASAR_LOG_LEVEL_CAT( - DEBUG, "LLVMBasedICFG", - "Function already visited or only declaration: " << F->getName()); - return true; - } - - assert(Res != nullptr); - - // add a node for function F to the call graph (if not present already) - std::ignore = CGBuilder.addFunctionVertex(F); - - bool FixpointReached = true; - - // iterate all instructions of the current function - Resolver::FunctionSetTy PossibleTargets; - for (const auto &I : llvm::instructions(F)) { - if (const auto *CS = llvm::dyn_cast(&I)) { - Res->preCall(&I); - - // check if function call can be resolved statically - if (CS->getCalledFunction() != nullptr) { - PossibleTargets.insert(CS->getCalledFunction()); - - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Found static call-site: " - << " " << llvmIRToString(CS)); - } else { - // still try to resolve the called function statically - const llvm::Value *SV = CS->getCalledOperand()->stripPointerCasts(); - const llvm::Function *ValueFunction = - !SV->hasName() ? nullptr : IRDB->getFunction(SV->getName()); - if (ValueFunction) { - PossibleTargets.insert(ValueFunction); - PHASAR_LOG_LEVEL_CAT( - DEBUG, "LLVMBasedICFG", - "Found static call-site: " << llvmIRToString(CS)); - } else { - if (llvm::isa(SV)) { - continue; - } - // the function call must be resolved dynamically - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Found dynamic call-site: " - << " " << llvmIRToString(CS)); - IndirectCalls[CS] = 0; - std::ignore = CGBuilder.addInstructionVertex(CS); - - FixpointReached = false; - continue; - } - } - - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Found " << PossibleTargets.size() - << " possible target(s)"); - - Res->handlePossibleTargets(CS, PossibleTargets); - - auto *CallSiteId = CGBuilder.addInstructionVertex(CS); - - // Insert possible target inside the graph and add the link with - // the current function - for (const auto *PossibleTarget : PossibleTargets) { - CGBuilder.addCallEdge(CS, CallSiteId, PossibleTarget); - - FunctionWL.push_back(PossibleTarget); - } - - Res->postCall(&I); - } else { - Res->otherInst(&I); - } - PossibleTargets.clear(); - } - - return FixpointReached; -} - -static bool internalIsVirtualFunctionCall(const llvm::Instruction *Inst, - const LLVMVFTableProvider &VTP) { - assert(Inst != nullptr); - const auto *CallSite = llvm::dyn_cast(Inst); - if (!CallSite) { - return false; - } - // check potential receiver type - const auto *RecType = getReceiverType(CallSite); - if (!RecType) { - return false; - } - if (!VTP.hasVFTable(RecType)) { - return false; - } - return getVFTIndex(CallSite) >= 0; -} - -bool LLVMBasedICFG::Builder::constructDynamicCall(const llvm::Instruction *CS) { - bool NewTargetsFound = false; - // Find vertex of calling function. - - auto *Callees = CGBuilder.getInstVertexOrNull(CS); - - if (!Callees) { - llvm::report_fatal_error( - "constructDynamicCall: Did not find vertex of calling function " + - CS->getFunction()->getName() + " at callsite " + llvmIRToString(CS)); - } - - if (const auto *CallSite = llvm::dyn_cast(CS)) { - Res->preCall(CallSite); - - // the function call must be resolved dynamically - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Looking into dynamic call-site: "); - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", " " << llvmIRToString(CS)); - // call the resolve routine - - assert(VTP != nullptr); - auto PossibleTargets = internalIsVirtualFunctionCall(CallSite, *VTP) - ? Res->resolveVirtualCall(CallSite) - : Res->resolveFunctionPointer(CallSite); - - assert(IndirectCalls.count(CallSite)); - - auto &NumIndCalls = IndirectCalls[CallSite]; - - if (NumIndCalls < PossibleTargets.size()) { - PHASAR_LOG_LEVEL_CAT(DEBUG, "LLVMBasedICFG", - "Found " << PossibleTargets.size() - NumIndCalls - << " new possible target(s)"); - NumIndCalls = PossibleTargets.size(); - NewTargetsFound = true; - } - if (!NewTargetsFound) { - return NewTargetsFound; - } - - // Throw out already found targets - for (const auto *Tgt : *Callees) { - PossibleTargets.erase(Tgt); - } - - Res->handlePossibleTargets(CallSite, PossibleTargets); - // Insert possible target inside the graph and add the link with - // the current function - for (const auto *PossibleTarget : PossibleTargets) { - CGBuilder.addCallEdge(CallSite, Callees, PossibleTarget); - FunctionWL.push_back(PossibleTarget); - } - - Res->postCall(CallSite); + auto *EntryFun = GlobalCtorsDtorsModel::buildModel(*IRDB, EntryPoints); + this->CG = buildLLVMBasedCallGraph(*IRDB, CGResolver, {EntryFun}, S); } else { - Res->otherInst(CS); + this->CG = buildLLVMBasedCallGraph(*IRDB, CGResolver, EntryPoints, S); } - - return NewTargetsFound; -} - -void LLVMBasedICFG::initialize(LLVMProjectIRDB *IRDB, Resolver &CGResolver, - llvm::ArrayRef EntryPoints, - const LLVMVFTableProvider &VTP, Soundness S, - bool IncludeGlobals) { - Builder B{IRDB, &VTP, &CGResolver}; - - B.initEntryPoints(EntryPoints); - B.initGlobalsAndWorkList(this, IncludeGlobals); - - PHASAR_LOG_LEVEL_CAT( - INFO, "LLVMBasedICFG", - "Starting ICFG construction " - << std::chrono::steady_clock::now().time_since_epoch().count()); - - this->CG = B.buildCallGraph(S); - - PHASAR_LOG_LEVEL_CAT( - INFO, "LLVMBasedICFG", - "Finished ICFG construction " - << std::chrono::steady_clock::now().time_since_epoch().count()); } LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, @@ -351,7 +61,7 @@ LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, } auto CGRes = Resolver::create(CGType, IRDB, &VTP, TH, PT); - initialize(IRDB, *CGRes, EntryPoints, VTP, S, IncludeGlobals); + initialize(IRDB, *CGRes, EntryPoints, S, IncludeGlobals); } LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, @@ -359,7 +69,8 @@ LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, Soundness S, bool IncludeGlobals) : IRDB(IRDB), VTP(*IRDB) { assert(IRDB != nullptr); - initialize(IRDB, CGResolver, EntryPoints, VTP, S, IncludeGlobals); + + initialize(IRDB, CGResolver, EntryPoints, S, IncludeGlobals); } LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, @@ -368,14 +79,15 @@ LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, Resolver &CGResolver, Soundness S, bool IncludeGlobals) : IRDB(IRDB), VTP(std::move(VTP)) { assert(IRDB != nullptr); - initialize(IRDB, CGResolver, EntryPoints, this->VTP, S, IncludeGlobals); + initialize(IRDB, CGResolver, EntryPoints, S, IncludeGlobals); } -LLVMBasedICFG::LLVMBasedICFG(CallGraph CG, LLVMProjectIRDB *IRDB) +LLVMBasedICFG::LLVMBasedICFG(CallGraph CG, + const LLVMProjectIRDB *IRDB) : CG(std::move(CG)), IRDB(IRDB), VTP(*IRDB) {} -LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, - const nlohmann::json &SerializedCG) +LLVMBasedICFG::LLVMBasedICFG(const LLVMProjectIRDB *IRDB, + const CallGraphData &SerializedCG) : CG(CallGraph::deserialize( SerializedCG, [IRDB](llvm::StringRef Name) { return IRDB->getFunction(Name); }, @@ -384,19 +96,6 @@ LLVMBasedICFG::LLVMBasedICFG(LLVMProjectIRDB *IRDB, LLVMBasedICFG::~LLVMBasedICFG() = default; -bool LLVMBasedICFG::isPhasarGenerated(const llvm::Function &F) noexcept { - if (F.hasName()) { - llvm::StringRef FunctionName = F.getName(); - return llvm::StringSwitch(FunctionName) - .Cases(GlobalCRuntimeModelName, GlobalCRuntimeDtorModelName, - GlobalCRuntimeDtorsCallerName, - GlobalCRuntimeUserEntrySelectorName, true) - .Default(false); - } - - return false; -} - [[nodiscard]] FunctionRange LLVMBasedICFG::getAllFunctionsImpl() const { return IRDB->getAllFunctions(); } @@ -412,7 +111,7 @@ bool LLVMBasedICFG::isPhasarGenerated(const llvm::Function &F) noexcept { } [[nodiscard]] bool LLVMBasedICFG::isVirtualFunctionCallImpl(n_t Inst) const { - return internalIsVirtualFunctionCall(Inst, VTP); + return psr::isVirtualCall(Inst, VTP); } [[nodiscard]] auto LLVMBasedICFG::allNonCallStartNodesImpl() const @@ -454,7 +153,13 @@ void LLVMBasedICFG::printImpl(llvm::raw_ostream &OS) const { [](n_t CS) { return llvmIRToStableString(CS); }); } -[[nodiscard]] nlohmann::json LLVMBasedICFG::getAsJsonImpl() const { +void LLVMBasedICFG::printAsJsonImpl(llvm::raw_ostream &OS) const { + CG.printAsJson( + OS, [](f_t F) { return F->getName().str(); }, + [this](n_t Inst) { return IRDB->getInstructionId(Inst); }); +} + +nlohmann::json LLVMBasedICFG::getAsJsonImpl() const { return CG.getAsJson( [](f_t F) { return F->getName().str(); }, [this](n_t Inst) { return IRDB->getInstructionId(Inst); }); diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp index 80b99a71a..f825f5254 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/NOResolver.cpp @@ -22,14 +22,9 @@ using namespace psr; namespace psr { -NOResolver::NOResolver(const LLVMProjectIRDB *IRDB) : Resolver(IRDB) {} - -void NOResolver::preCall(const llvm::Instruction *Inst) {} - -void NOResolver::handlePossibleTargets(const llvm::CallBase *CallSite, - FunctionSetTy &PossibleTargets) {} - -void NOResolver::postCall(const llvm::Instruction *Inst) {} +NOResolver::NOResolver(const LLVMProjectIRDB *IRDB, + const LLVMVFTableProvider *VTP) + : Resolver(IRDB, VTP) {} auto NOResolver::resolveVirtualCall(const llvm::CallBase * /*CallSite*/) -> FunctionSetTy { @@ -41,8 +36,6 @@ auto NOResolver::resolveFunctionPointer(const llvm::CallBase * /*CallSite*/) return {}; } -void NOResolver::otherInst(const llvm::Instruction *Inst) {} - std::string NOResolver::str() const { return "NOResolver"; } } // namespace psr diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp index c1f783d54..fb2d8a896 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/OTFResolver.cpp @@ -34,8 +34,6 @@ OTFResolver::OTFResolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP, LLVMAliasInfoRef PT) : Resolver(IRDB, VTP), PT(PT) {} -void OTFResolver::preCall(const llvm::Instruction *Inst) {} - void OTFResolver::handlePossibleTargets(const llvm::CallBase *CallSite, FunctionSetTy &CalleeTargets) { // if we have no inter-procedural points-to information, use call-graph @@ -67,8 +65,6 @@ void OTFResolver::handlePossibleTargets(const llvm::CallBase *CallSite, } } -void OTFResolver::postCall(const llvm::Instruction *Inst) {} - auto OTFResolver::resolveVirtualCall(const llvm::CallBase *CallSite) -> FunctionSetTy { FunctionSetTy PossibleCallTargets; diff --git a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp index 745a4e866..e55edefce 100644 --- a/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp +++ b/lib/PhasarLLVM/ControlFlow/Resolver/Resolver.cpp @@ -108,14 +108,31 @@ bool psr::isConsistentCall(const llvm::CallBase *CallSite, return true; } -namespace psr { +bool psr::isVirtualCall(const llvm::Instruction *Inst, + const LLVMVFTableProvider &VTP) { + assert(Inst != nullptr); + const auto *CallSite = llvm::dyn_cast(Inst); + if (!CallSite) { + return false; + } + // check potential receiver type + const auto *RecType = getReceiverType(CallSite); + if (!RecType) { + return false; + } -Resolver::Resolver(const LLVMProjectIRDB *IRDB) : IRDB(IRDB), VTP(nullptr) { - assert(IRDB != nullptr); + if (!VTP.hasVFTable(RecType)) { + return false; + } + return getVFTIndex(CallSite) >= 0; } +namespace psr { + Resolver::Resolver(const LLVMProjectIRDB *IRDB, const LLVMVFTableProvider *VTP) - : IRDB(IRDB), VTP(VTP) {} + : IRDB(IRDB), VTP(VTP) { + assert(VTP != nullptr); +} const llvm::Function * Resolver::getNonPureVirtualVFTEntry(const llvm::StructType *T, unsigned Idx, @@ -140,6 +157,14 @@ void Resolver::handlePossibleTargets(const llvm::CallBase *CallSite, void Resolver::postCall(const llvm::Instruction *Inst) {} +auto Resolver::resolveIndirectCall(const llvm::CallBase *CallSite) + -> FunctionSetTy { + if (VTP && isVirtualCall(CallSite, *VTP)) { + return resolveVirtualCall(CallSite); + } + return resolveFunctionPointer(CallSite); +} + auto Resolver::resolveFunctionPointer(const llvm::CallBase *CallSite) -> FunctionSetTy { // we may wish to optimise this function @@ -170,7 +195,7 @@ std::unique_ptr Resolver::create(CallGraphAnalysisType Ty, switch (Ty) { case CallGraphAnalysisType::NORESOLVE: - return std::make_unique(IRDB); + return std::make_unique(IRDB, VTP); case CallGraphAnalysisType::CHA: assert(TH != nullptr); return std::make_unique(IRDB, VTP, TH); diff --git a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp index e853bf724..ed64e4784 100644 --- a/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp +++ b/lib/PhasarLLVM/Passes/GeneralStatisticsAnalysis.cpp @@ -23,6 +23,7 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/Casting.h" +#include "llvm/Support/Format.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/raw_ostream.h" @@ -289,31 +290,55 @@ GeneralStatistics::getRetResInstructions() const { } void GeneralStatistics::printAsJson(llvm::raw_ostream &OS) const { - OS << getAsJson() << '\n'; + nlohmann::json Json; + + Json["ModuleName"] = ModuleName; + Json["Instructions"] = Instructions; + Json["Functions"] = Functions; + Json["ExternalFunctions"] = ExternalFunctions; + Json["FunctionDefinitions"] = FunctionDefinitions; + Json["AddressTakenFunctions"] = AddressTakenFunctions; + Json["Globals"] = Globals; + Json["GlobalConsts"] = GlobalConsts; + Json["GlobalVariables"] = Globals - GlobalConsts; + Json["ExternalGlobals"] = ExternalGlobals; + Json["GlobalsDefinitions"] = GlobalsDefinitions; + Json["AllocaInstructions"] = AllocaInstructions.size(); + Json["CallSites"] = CallSites; + Json["IndirectCallSites"] = IndCalls; + Json["NumInlineAssembly"] = NumInlineAsm; + Json["MemoryIntrinsics"] = MemIntrinsics; + Json["DebugIntrinsics"] = DebugIntrinsics; + Json["Switches"] = Switches; + Json["GetElementPtrs"] = GetElementPtrs; + Json["PhiNodes"] = PhiNodes; + Json["LandingPads"] = LandingPads; + Json["BasicBlocks"] = BasicBlocks; + Json["TotalNumPredecessorBBs"] = TotalNumPredecessorBBs; + Json["Branches"] = Branches; + Json["AvgPredPerBasicBlock"] = + double(TotalNumPredecessorBBs) / double(BasicBlocks); + Json["MaxPredPerBasicBlock"] = MaxNumPredecessorBBs; + Json["AvgSuccPerBasicBlock"] = + double(TotalNumSuccessorBBs) / double(BasicBlocks); + Json["MaxSuccPerBasicBlock"] = MaxNumSuccessorBBs; + Json["AvgOperandsPerInst"] = double(TotalNumOperands) / double(Instructions); + Json["MaxNumOperandsPerInst"] = MaxNumOperands; + Json["AvgUsesPerInst"] = double(TotalNumUses) / double(Instructions); + Json["MaxUsesPerInst"] = MaxNumUses; + Json["NumInstWithMultipleUses"] = NumInstWithMultipleUses; + Json["NonVoidInsts"] = NonVoidInsts; + Json["NumInstsUsedOutsideBB"] = NumInstsUsedOutsideBB; + + OS << Json << '\n'; } nlohmann::json GeneralStatistics::getAsJson() const { - nlohmann::json J; - J["ModuleName"] = ModuleName; - J["Instructions"] = Instructions; - J["Functions"] = Functions; - J["ExternalFunctions"] = ExternalFunctions; - J["FunctionDefinitions"] = FunctionDefinitions; - J["AddressTakenFunctions"] = AddressTakenFunctions; - J["AllocaInstructions"] = AllocaInstructions.size(); - J["CallSites"] = CallSites; - J["IndirectCallSites"] = IndCalls; - J["MemoryIntrinsics"] = MemIntrinsics; - J["DebugIntrinsics"] = DebugIntrinsics; - J["InlineAssembly"] = NumInlineAsm; - J["GlobalVariables"] = Globals; - J["Branches"] = Branches; - J["GetElementPtrs"] = GetElementPtrs; - J["BasicBlocks"] = BasicBlocks; - J["PhiNodes"] = PhiNodes; - J["LandingPads"] = LandingPads; - J["GlobalConsts"] = GlobalConsts; - return J; + std::string GeneralStatisticsAsString; + llvm::raw_string_ostream Stream(GeneralStatisticsAsString); + printAsJson(Stream); + + return nlohmann::json::parse(GeneralStatisticsAsString); } } // namespace psr @@ -377,6 +402,7 @@ llvm::raw_ostream &psr::operator<<(llvm::raw_ostream &OS, << AlignNum("Phi Nodes", Statistics.PhiNodes) << AlignNum("LandingPads", Statistics.LandingPads) << AlignNum("Basic Blocks", Statistics.BasicBlocks) + << AlignNum("Branches", Statistics.Branches) << AlignNum("Avg #pred per BasicBlock", Statistics.TotalNumPredecessorBBs, Statistics.BasicBlocks) << AlignNum("Max #pred per BasicBlock", diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp index 83e796b6b..40b95feaa 100644 --- a/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSet.cpp @@ -723,8 +723,35 @@ nlohmann::json LLVMAliasSet::getAsJson() const { return J; } +LLVMAliasSetData LLVMAliasSet::getLLVMAliasSetData() const { + LLVMAliasSetData Data; + + /// Serialize the AliasSets + for (const AliasSetTy *PTS : Owner.getAllAliasSets()) { + + std::vector PtsJson{}; + for (const auto *Alias : *PTS) { + auto Id = getMetaDataID(Alias); + if (Id != "-1") { + PtsJson.push_back(std::move(Id)); + } + } + if (!PtsJson.empty()) { + Data.AliasSets.push_back(std::move(PtsJson)); + } + } + + /// Serialize the AnalyzedFunctions + for (const auto *F : AnalyzedFunctions) { + Data.AnalyzedFunctions.push_back(F->getName().str()); + } + + return Data; +} + void LLVMAliasSet::printAsJson(llvm::raw_ostream &OS) const { - OS << getAsJson(); + LLVMAliasSetData Data = getLLVMAliasSetData(); + Data.printAsJson(OS); } void LLVMAliasSet::print(llvm::raw_ostream &OS) const { diff --git a/lib/PhasarLLVM/Pointer/LLVMAliasSetData.cpp b/lib/PhasarLLVM/Pointer/LLVMAliasSetData.cpp new file mode 100644 index 000000000..4f807c581 --- /dev/null +++ b/lib/PhasarLLVM/Pointer/LLVMAliasSetData.cpp @@ -0,0 +1,56 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#include "phasar/PhasarLLVM/Pointer/LLVMAliasSetData.h" + +#include "phasar/Utils/IO.h" +#include "phasar/Utils/NlohmannLogging.h" + +namespace psr { + +static LLVMAliasSetData getDataFromJson(const nlohmann::json &Json) { + LLVMAliasSetData Data; + + for (const auto &Value : Json["AliasSets"]) { + Data.AliasSets.push_back(Value.get>()); + } + + for (const auto &Value : Json["AnalyzedFunctions"]) { + Data.AnalyzedFunctions.push_back(Value.get()); + } + + return Data; +} + +void LLVMAliasSetData::printAsJson(llvm::raw_ostream &OS) { + nlohmann::json JSON; + + for (const auto &Curr : AliasSets) { + JSON["AliasSets"].push_back(Curr); + } + + for (const auto &Curr : AnalyzedFunctions) { + JSON["AnalyzedFunctions"].push_back(Curr); + } + + OS << JSON << '\n'; +} + +LLVMAliasSetData LLVMAliasSetData::deserializeJson(const llvm::Twine &Path) { + return getDataFromJson(readJsonFile(Path)); +} + +LLVMAliasSetData +LLVMAliasSetData::loadJsonString(llvm::StringRef JsonAsString) { + nlohmann::json Data = + nlohmann::json::parse(JsonAsString.begin(), JsonAsString.end()); + return getDataFromJson(Data); +} + +} // namespace psr diff --git a/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp index fd01a0759..be0f02c07 100644 --- a/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.cpp @@ -10,6 +10,7 @@ #include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h" #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" #include "phasar/Utils/Logger.h" #include "phasar/Utils/Utilities.h" @@ -17,8 +18,10 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallBitVector.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/IR/DebugInfo.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" @@ -29,6 +32,10 @@ #include "llvm/Support/raw_ostream.h" #include +#include +#include +#include +#include namespace psr { using ClassType = DIBasedTypeHierarchy::ClassType; @@ -103,8 +110,13 @@ buildTypeGraph(llvm::ArrayRef VertexTypes, if (const auto *Inheritenace = llvm::dyn_cast(Fld); Inheritenace && Inheritenace->getTag() == llvm::dwarf::DW_TAG_inheritance) { - auto BaseIdx = TypeToVertex.lookup(Inheritenace->getBaseType()); - assert(BaseIdx != 0 || VertexTypes[0] == Inheritenace->getBaseType()); + const auto *Base = Inheritenace->getBaseType(); + while (Base->getTag() == llvm::dwarf::DW_TAG_typedef) { + Base = llvm::cast(Base)->getBaseType(); + } + + auto BaseIdx = TypeToVertex.lookup(Base); + assert(BaseIdx != 0 || VertexTypes[0] == Base); TG.DerivedTypesOf[BaseIdx].push_back(DerivedIdx); TG.Roots.reset(DerivedIdx); @@ -153,6 +165,11 @@ static void buildTypeHierarchy( } } +static llvm::StringRef getCompositeTypeName(const llvm::DICompositeType *Ty) { + auto Ident = Ty->getIdentifier(); + return Ident.empty() ? Ty->getName() : Ident; +} + DIBasedTypeHierarchy::DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB) { // -- Find all types { @@ -167,11 +184,24 @@ DIBasedTypeHierarchy::DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB) { // -- Filter all struct- or class types + // NOLINTNEXTLINE(cppcoreguidelines-avoid-c-arrays,modernize-avoid-c-arrays) + static constexpr llvm::dwarf::Tag DwarfTags[] = { + llvm::dwarf::DW_TAG_class_type, + llvm::dwarf::DW_TAG_structure_type, + llvm::dwarf::DW_TAG_union_type, + }; + for (const auto *Ty : DIF.types()) { if (const auto *Composite = llvm::dyn_cast(Ty)) { - TypeToVertex.try_emplace(Composite, VertexTypes.size()); - VertexTypes.push_back(Composite); - NameToType.try_emplace(Composite->getName(), Composite); + if (!llvm::is_contained(DwarfTags, Composite->getTag())) { + continue; + } + if (TypeToVertex.try_emplace(Composite, VertexTypes.size()).second) { + VertexTypes.push_back(Composite); + NameToType.try_emplace(getCompositeTypeName(Composite), Composite); + + assert(!getCompositeTypeName(Composite).empty()); + } } } @@ -189,6 +219,81 @@ DIBasedTypeHierarchy::DIBasedTypeHierarchy(const LLVMProjectIRDB &IRDB) { buildTypeHierarchy(TG, VertexTypes, TransitiveDerivedIndex, Hierarchy); } +static const llvm::DICompositeType * +stringToDICompositeType(const llvm::DebugInfoFinder &DIF, + llvm::StringRef DITypeName) { + const llvm::DICompositeType *ByName = nullptr; + bool HasUniqueByName = false; + for (const auto *Type : DIF.types()) { + if (const auto *DICT = llvm::dyn_cast(Type)) { + auto Ident = DICT->getIdentifier(); + if (Ident == DITypeName) { + return DICT; + } + if (DICT->getName() == DITypeName) { + HasUniqueByName = ByName == nullptr; + ByName = DICT; + } + } + } + if (HasUniqueByName) { + return ByName; + } + + llvm::report_fatal_error("DIType doesn't exist: " + DITypeName); +} + +static const llvm::DIType *stringToDIType(const llvm::DebugInfoFinder &DIF, + llvm::StringRef DITypeName) { + for (const auto *Type : DIF.types()) { + if (Type->getName() == DITypeName) { + return Type; + } + } + + llvm::report_fatal_error("DIType doesn't exist " + DITypeName); +} + +DIBasedTypeHierarchy::DIBasedTypeHierarchy( + const LLVMProjectIRDB *IRDB, const DIBasedTypeHierarchyData &SerializedData) + : TransitiveDerivedIndex(SerializedData.TransitiveDerivedIndex) { + + llvm::DebugInfoFinder DIF; + const auto *Module = IRDB->getModule(); + DIF.processModule(*Module); + + VertexTypes.reserve(SerializedData.VertexTypes.size()); + TypeToVertex.reserve(SerializedData.VertexTypes.size()); + size_t Idx = 0; + for (const auto &Curr : SerializedData.VertexTypes) { + const auto *Ty = stringToDICompositeType(DIF, Curr); + VertexTypes.push_back(Ty); + TypeToVertex.try_emplace(Ty, Idx); + NameToType.try_emplace(Curr, Ty); + + ++Idx; + } + + Hierarchy.reserve(SerializedData.Hierarchy.size()); + for (const auto &Curr : SerializedData.Hierarchy) { + Hierarchy.push_back(stringToDIType(DIF, Curr)); + } + + for (const auto &Curr : SerializedData.VTables) { + std::vector CurrVTable; + + CurrVTable.reserve(Curr.size()); + for (const auto &FuncName : Curr) { + if (FuncName == LLVMVFTable::NullFunName) { + CurrVTable.push_back(nullptr); + } + CurrVTable.push_back(IRDB->getFunction(FuncName)); + } + + VTables.emplace_back(std::move(CurrVTable)); + } +} + auto DIBasedTypeHierarchy::subTypesOf(size_t TypeIdx) const noexcept -> llvm::iterator_range { const auto *Data = Hierarchy.data(); @@ -236,11 +341,51 @@ void DIBasedTypeHierarchy::print(llvm::raw_ostream &OS) const { } } -[[nodiscard]] nlohmann::json DIBasedTypeHierarchy::getAsJson() const { +[[nodiscard]] [[deprecated("Please use printAsJson() instead")]] nlohmann::json +DIBasedTypeHierarchy::getAsJson() const { /// TODO: implement llvm::report_fatal_error("Not implemented"); } +DIBasedTypeHierarchyData DIBasedTypeHierarchy::getTypeHierarchyData() const { + DIBasedTypeHierarchyData Data; + + Data.VertexTypes.reserve(VertexTypes.size()); + + for (const auto &Curr : VertexTypes) { + Data.VertexTypes.push_back(getTypeName(Curr).str()); + } + + Data.TransitiveDerivedIndex = TransitiveDerivedIndex; + + Data.Hierarchy.reserve(Hierarchy.size()); + for (const auto &Curr : Hierarchy) { + Data.Hierarchy.push_back(Curr->getName().str()); + } + + for (const auto &Curr : VTables) { + std::vector CurrVTableAsString; + CurrVTableAsString.reserve(Curr.getAllFunctions().size()); + + for (const auto &Func : Curr.getAllFunctions()) { + if (Func) { + CurrVTableAsString.push_back(Func->getName().str()); + continue; + } + CurrVTableAsString.emplace_back(LLVMVFTable::NullFunName); + } + + Data.VTables.push_back(std::move(CurrVTableAsString)); + } + + return Data; +} + +void DIBasedTypeHierarchy::printAsJson(llvm::raw_ostream &OS) const { + DIBasedTypeHierarchyData Data = getTypeHierarchyData(); + Data.printAsJson(OS); +} + void DIBasedTypeHierarchy::printAsDot(llvm::raw_ostream &OS) const { OS << "digraph TypeHierarchy{\n"; scope_exit CloseBrace = [&OS] { OS << "}\n"; }; diff --git a/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.cpp b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.cpp new file mode 100644 index 000000000..b1fc17cb2 --- /dev/null +++ b/lib/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.cpp @@ -0,0 +1,76 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyData.h" + +#include "phasar/Utils/IO.h" +#include "phasar/Utils/NlohmannLogging.h" + +#include "llvm/ADT/StringRef.h" + +#include +#include +#include + +namespace psr { + +static DIBasedTypeHierarchyData getDataFromJson(const nlohmann::json &Json) { + DIBasedTypeHierarchyData Data; + + Data.VertexTypes = Json["VertexTypes"].get>(); + + Data.TransitiveDerivedIndex = + Json["TransitiveDerivedIndex"] + .get>>(); + + Data.Hierarchy = Json["Hierarchy"].get>(); + + for (const auto &CurrVTable : Json["VTables"]) { + auto &DataPos = Data.VTables.emplace_back(); + + for (const auto &CurrVFunc : CurrVTable) { + DataPos.push_back(CurrVFunc.get()); + } + } + + return Data; +} + +void DIBasedTypeHierarchyData::printAsJson(llvm::raw_ostream &OS) { + nlohmann::json Json; + + Json["VertexTypes"] = VertexTypes; + Json["TransitiveDerivedIndex"] = TransitiveDerivedIndex; + Json["Hierarchy"] = Hierarchy; + + auto &JVTables = Json["VTables"]; + for (const auto &CurrVTable : VTables) { + auto &DataPos = JVTables.emplace_back(); + + for (const auto &CurrVFunc : CurrVTable) { + DataPos.push_back(CurrVFunc); + } + } + + OS << Json << '\n'; +} + +DIBasedTypeHierarchyData +DIBasedTypeHierarchyData::deserializeJson(const llvm::Twine &Path) { + return getDataFromJson(readJsonFile(Path)); +} + +DIBasedTypeHierarchyData +DIBasedTypeHierarchyData::loadJsonString(llvm::StringRef JsonAsString) { + nlohmann::json Data = + nlohmann::json::parse(JsonAsString.begin(), JsonAsString.end()); + return getDataFromJson(Data); +} + +} // namespace psr diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp index 2fe8ac954..23dbdcc28 100644 --- a/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.cpp @@ -24,9 +24,10 @@ #include "phasar/Utils/PAMMMacros.h" #include "phasar/Utils/Utilities.h" -#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Demangle/Demangle.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" #include "llvm/IR/InstIterator.h" @@ -74,6 +75,63 @@ LLVMTypeHierarchy::LLVMTypeHierarchy(const LLVMProjectIRDB &IRDB) { buildLLVMTypeHierarchy(*IRDB.getModule()); } +LLVMTypeHierarchy::LLVMTypeHierarchy( + const LLVMProjectIRDB &IRDB, const LLVMTypeHierarchyData &SerializedData) { + + llvm::StringMap NameToStructType; + const auto &IRDBModule = IRDB.getModule(); + + VisitedModules.insert(IRDBModule); + auto StructTypes = IRDBModule->getIdentifiedStructTypes(); + + // find all struct types by name + for (const auto &SerElement : SerializedData.TypeGraph) { + bool MatchFound = false; + + for (const auto &StructTypeElement : StructTypes) { + if (SerElement.getKey() == StructTypeElement->getName()) { + NameToStructType.try_emplace(SerElement.getKey(), StructTypeElement); + MatchFound = true; + break; + } + } + + if (!MatchFound) { + PHASAR_LOG_LEVEL(WARNING, + "No matching StructType found for Type with name: " + << SerElement.getKey()); + } + } + + // add all vertices + for (const auto &Curr : NameToStructType) { + const auto &StructType = Curr.getValue(); + auto Vertex = boost::add_vertex(TypeGraph); + TypeVertexMap[StructType] = Vertex; + TypeGraph[Vertex] = VertexProperties(StructType); + TypeVFTMap[StructType] = getVirtualFunctions(*IRDBModule, *StructType); + } + + // add all edges + for (const auto &SerElement : SerializedData.TypeGraph) { + const auto *SrcType = NameToStructType[SerElement.getKey()]; + if (!SrcType) { + continue; + } + auto Vtx = TypeVertexMap.at(SrcType); + for (const auto &CurrEdge : SerElement.getValue()) { + const auto *DestType = NameToStructType[CurrEdge]; + if (!SrcType) { + continue; + } + auto DestVtx = TypeVertexMap.at(DestType); + + TypeGraph[Vtx].ReachableTypes.insert(DestType); + boost::add_edge(Vtx, DestVtx, TypeGraph); + } + } +} + LLVMTypeHierarchy::LLVMTypeHierarchy(const llvm::Module &M) { PHASAR_LOG_LEVEL_CAT(INFO, "LLVMTypeHierarchy", "Construct type hierarchy"); buildLLVMTypeHierarchy(M); @@ -162,6 +220,7 @@ LLVMTypeHierarchy::getSubTypes(const llvm::Module & /*M*/, // find corresponding type info variable std::vector SubTypes; std::string ClearName = removeStructOrClassPrefix(Type); + if (auto It = ClearNameTIMap.find(ClearName); It != ClearNameTIMap.end()) { const auto *TI = It->second; if (!TI->hasInitializer()) { @@ -179,9 +238,9 @@ LLVMTypeHierarchy::getSubTypes(const llvm::Module & /*M*/, if (Name.find(TypeInfoPrefix) != llvm::StringRef::npos) { auto ClearName = removeTypeInfoPrefix(llvm::demangle(Name.str())); - if (auto TyIt = ClearNameTypeMap.find(ClearName); - TyIt != ClearNameTypeMap.end()) { - SubTypes.push_back(TyIt->second); + if (auto TypeIt = ClearNameTypeMap.find(ClearName); + TypeIt != ClearNameTypeMap.end()) { + SubTypes.push_back(TypeIt->second); } } } @@ -247,10 +306,12 @@ void LLVMTypeHierarchy::constructHierarchy(const llvm::Module &M) { TypeVFTMap[StructType] = getVirtualFunctions(M, *StructType); } } + // construct the edges between a type and its subtypes for (auto *StructType : StructTypes) { // use type information to check if it is really a subtype auto SubTypes = getSubTypes(M, *StructType); + for (const auto *SubType : SubTypes) { boost::add_edge(TypeVertexMap[SubType], TypeVertexMap[StructType], TypeGraph); @@ -393,7 +454,20 @@ void LLVMTypeHierarchy::printAsDot(llvm::raw_ostream &OS) const { } void LLVMTypeHierarchy::printAsJson(llvm::raw_ostream &OS) const { - OS << getAsJson(); + LLVMTypeHierarchyData Data; + Data.PhasarConfigJsonTypeHierarchyID = + PhasarConfig::JsonTypeHierarchyID().str(); + + // iterate all graph vertices + for (auto Vtx : boost::make_iterator_range(boost::vertices(TypeGraph))) { + // iterate all out edges of vertex vi_v + auto &SerTypes = Data.TypeGraph[TypeGraph[Vtx].getTypeName()]; + for (const auto &CurrReachable : TypeGraph[Vtx].ReachableTypes) { + SerTypes.push_back(CurrReachable->getName().str()); + } + } + + Data.printAsJson(OS); } // void LLVMTypeHierarchy::printGraphAsDot(ostream &out) { diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.cpp new file mode 100644 index 000000000..5247e8f17 --- /dev/null +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.cpp @@ -0,0 +1,66 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchyData.h" + +#include "phasar/Utils/IO.h" +#include "phasar/Utils/NlohmannLogging.h" + +namespace psr { + +static LLVMTypeHierarchyData getDataFromJson(const nlohmann::json &Json) { + LLVMTypeHierarchyData Data; + Data.PhasarConfigJsonTypeHierarchyID = + Json["PhasarConfigJsonTypeHierarchyID"]; + + for (const auto &[Key, ValueArray] : + Json[Data.PhasarConfigJsonTypeHierarchyID] + .get()) { + Data.TypeGraph.try_emplace(Key, std::vector{}); + + for (const auto &CurrInnerType : ValueArray) { + for (const auto &CurrString : CurrInnerType) { + Data.TypeGraph[Key].push_back(CurrString.get()); + } + } + } + + return Data; +} + +void LLVMTypeHierarchyData::printAsJson(llvm::raw_ostream &OS) { + nlohmann::json Json; + + Json["PhasarConfigJsonTypeHierarchyID"] = PhasarConfigJsonTypeHierarchyID; + + for (const auto &Curr : TypeGraph) { + auto &DataPos = + Json[PhasarConfigJsonTypeHierarchyID][Curr.getKey()].emplace_back(); + + for (const auto &CurrTypeName : Curr.getValue()) { + DataPos.push_back(CurrTypeName); + } + } + + OS << Json << '\n'; +} + +LLVMTypeHierarchyData +LLVMTypeHierarchyData::deserializeJson(const llvm::Twine &Path) { + return getDataFromJson(readJsonFile(Path)); +} + +LLVMTypeHierarchyData +LLVMTypeHierarchyData::loadJsonString(llvm::StringRef JsonAsString) { + nlohmann::json Data = + nlohmann::json::parse(JsonAsString.begin(), JsonAsString.end()); + return getDataFromJson(Data); +} + +} // namespace psr diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp index 76c2f7c82..2b6b443ef 100644 --- a/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTable.cpp @@ -9,9 +9,13 @@ #include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTable.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" +#include "phasar/Utils/NlohmannLogging.h" + #include "llvm/IR/Function.h" #include "llvm/IR/GlobalAlias.h" #include "llvm/IR/Operator.h" +#include "llvm/Support/raw_ostream.h" #include #include @@ -49,6 +53,26 @@ nlohmann::json LLVMVFTable::getAsJson() const { return J; } +[[nodiscard]] LLVMVFTableData LLVMVFTable::getVFTableData() const { + LLVMVFTableData Data; + + for (const auto &Curr : VFT) { + if (Curr) { + Data.VFT.push_back(Curr->getName().str()); + continue; + } + + Data.VFT.emplace_back(NullFunName); + } + + return Data; +} + +void LLVMVFTable::printAsJson(llvm::raw_ostream &OS) const { + LLVMVFTableData Data = getVFTableData(); + Data.printAsJson(OS); +} + std::vector LLVMVFTable::getVFVectorFromIRVTable(const llvm::ConstantStruct &VT) { std::vector VFS; diff --git a/lib/PhasarLLVM/TypeHierarchy/LLVMVFTableData.cpp b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTableData.cpp new file mode 100644 index 000000000..69d2a8da6 --- /dev/null +++ b/lib/PhasarLLVM/TypeHierarchy/LLVMVFTableData.cpp @@ -0,0 +1,47 @@ +/****************************************************************************** + * Copyright (c) 2024 Fabian Schiebel. + * All rights reserved. This program and the accompanying materials are made + * available under the terms of LICENSE.txt. + * + * Contributors: + * Maximilian Leo Huber and others + *****************************************************************************/ + +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMVFTableData.h" + +#include "phasar/Utils/IO.h" +#include "phasar/Utils/NlohmannLogging.h" + +namespace psr { + +static LLVMVFTableData getDataFromJson(const nlohmann::json &Json) { + LLVMVFTableData Data; + + for (const auto &Curr : Json["VFT"]) { + Data.VFT.push_back(Curr); + } + + return Data; +} + +void LLVMVFTableData::printAsJson(llvm::raw_ostream &OS) const { + nlohmann::json JSON; + + for (const auto &Curr : VFT) { + JSON["VFT"].push_back(Curr); + } + + OS << JSON << '\n'; +} + +LLVMVFTableData LLVMVFTableData::deserializeJson(const llvm::Twine &Path) { + return getDataFromJson(readJsonFile(Path)); +} + +LLVMVFTableData LLVMVFTableData::loadJsonString(llvm::StringRef JsonAsString) { + nlohmann::json Data = + nlohmann::json::parse(JsonAsString.begin(), JsonAsString.end()); + return getDataFromJson(Data); +} + +} // namespace psr diff --git a/tools/phasar-cli/Controller/AnalysisController.cpp b/tools/phasar-cli/Controller/AnalysisController.cpp index 44f25be72..4c206a15a 100644 --- a/tools/phasar-cli/Controller/AnalysisController.cpp +++ b/tools/phasar-cli/Controller/AnalysisController.cpp @@ -71,7 +71,7 @@ void AnalysisController::emitRequestedHelperAnalysisResults() { } if (EmitterOptions & AnalysisControllerEmitterOptions::EmitCGAsJson) { WithResultFileOrStdout("/psr-cg.json", - [&HA](auto &OS) { OS << HA.getICFG().getAsJson(); }); + [&HA](auto &OS) { HA.getICFG().printAsJson(OS); }); } if (EmitterOptions & diff --git a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobCtorDtorTest.cpp b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobCtorDtorTest.cpp index 0977c8189..5c1a295fc 100644 --- a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobCtorDtorTest.cpp +++ b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGGlobCtorDtorTest.cpp @@ -9,6 +9,7 @@ #include "phasar/Config/Configuration.h" #include "phasar/DataFlow/IfdsIde/Solver/IDESolver.h" +#include "phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -93,10 +94,9 @@ TEST_F(LLVMBasedICFGGlobCtorDtorTest, CtorTest) { // GlobalCtor->print(llvm::outs()); - ensureFunctionOrdering( - GlobalCtor, ICFG, - {{"_GLOBAL__sub_I_globals_ctor_1.cpp", "main"}, - {"main", LLVMBasedICFG::GlobalCRuntimeDtorModelName}}); + ensureFunctionOrdering(GlobalCtor, ICFG, + {{"_GLOBAL__sub_I_globals_ctor_1.cpp", "main"}, + {"main", GlobalCtorsDtorsModel::DtorModelName}}); } TEST_F(LLVMBasedICFGGlobCtorDtorTest, CtorTest2) { @@ -145,12 +145,11 @@ TEST_F(LLVMBasedICFGGlobCtorDtorTest, DtorTest1) { ensureFunctionOrdering( GlobalCtor, ICFG, {{"_GLOBAL__sub_I_globals_dtor_1.cpp", "main"}, - {"main", LLVMBasedICFG::GlobalCRuntimeDtorsCallerName.str() + + {"main", GlobalCtorsDtorsModel::DtorsCallerName.str() + ".globals_dtor_1_cpp.ll"}}); - auto *GlobalDtor = - IRDB.getFunction(LLVMBasedICFG::GlobalCRuntimeDtorsCallerName.str() + - ".globals_dtor_1_cpp.ll"); + auto *GlobalDtor = IRDB.getFunction( + GlobalCtorsDtorsModel::DtorsCallerName.str() + ".globals_dtor_1_cpp.ll"); ASSERT_NE(nullptr, GlobalDtor); diff --git a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGSerializationTest.cpp b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGSerializationTest.cpp index 35619ce5a..194872b8d 100644 --- a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGSerializationTest.cpp +++ b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGSerializationTest.cpp @@ -1,8 +1,15 @@ #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/ControlFlow/CallGraphData.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" #include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/IO.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" #include "TestConfig.h" #include "gtest/gtest.h" @@ -15,13 +22,19 @@ class LLVMBasedICFGGSerializationTest : public ::testing::Test { using namespace std::string_literals; psr::LLVMProjectIRDB IRDB(PathToLLFiles + IRFile); - psr::LLVMBasedICFG ICF(&IRDB, psr::CallGraphAnalysisType::OTF, {"main"s}); - auto Ser = ICF.getAsJson(); - psr::LLVMBasedICFG Deser(&IRDB, Ser); + std::string Ser; + // stream ICF data into a json file using the printAsJson() function + llvm::raw_string_ostream StringStream(Ser); + + ICF.printAsJson(StringStream); + + // deserialize data into CallGraphData object + psr::LLVMBasedICFG DeserializedICF(&IRDB, + psr::CallGraphData::loadJsonString(Ser)); - compareResults(ICF, Deser); + compareResults(ICF, DeserializedICF); } void compareResults(const psr::LLVMBasedICFG &Orig, diff --git a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGTest.cpp b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGTest.cpp index 249283fde..9b001834a 100644 --- a/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGTest.cpp +++ b/unittests/PhasarLLVM/ControlFlow/LLVMBasedICFGTest.cpp @@ -2,6 +2,7 @@ #include "phasar/Config/Configuration.h" #include "phasar/ControlFlow/CallGraphAnalysisType.h" +#include "phasar/PhasarLLVM/ControlFlow/GlobalCtorsDtorsModel.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedCFG.h" #include "phasar/PhasarLLVM/ControlFlow/LLVMBasedICFG.h" #include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" @@ -85,7 +86,7 @@ TEST(LLVMBasedICFGTest, StaticCallSite_2b) { const llvm::Function *CTOR = IRDB.getFunctionDefinition(LLVMBasedICFG::GlobalCRuntimeModelName); const llvm::Function *DTOR = - IRDB.getFunctionDefinition(LLVMBasedICFG::GlobalCRuntimeDtorModelName); + IRDB.getFunctionDefinition(GlobalCtorsDtorsModel::DtorModelName); ASSERT_TRUE(F); ASSERT_TRUE(FOO); ASSERT_TRUE(BAR); diff --git a/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp b/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp index 1da745937..431c82218 100644 --- a/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp +++ b/unittests/PhasarLLVM/Pointer/LLVMAliasSetSerializationTest.cpp @@ -39,7 +39,6 @@ static SetTy makeSet(const nlohmann::json &J) { } static void checkSer(const nlohmann::json &Ser, const GroundTruthTy &Gt) { - ASSERT_TRUE(Ser.count("AliasSets")); ASSERT_TRUE(Ser.count("AnalyzedFunctions")); @@ -77,18 +76,19 @@ static void analyze(llvm::StringRef File, const GroundTruthTy &Gt, ValueAnnotationPass::resetValueID(); LLVMProjectIRDB IRDB(unittest::PathToLLTestFiles + File); - // llvm::outs() << *IRDB.getWPAModule() << '\n'; - LLVMAliasSet PTS(&IRDB, false); LLVMTypeHierarchy TH(IRDB); LLVMBasedICFG ICF(&IRDB, CallGraphAnalysisType::OTF, {EntryPoint.str()}, &TH, &PTS); - auto Ser = PTS.getAsJson(); - checkSer(Ser, Gt); + std::string SerString; + llvm::raw_string_ostream StringStream(SerString); + PTS.printAsJson(StringStream); + nlohmann::json PrintAsJsonSer = nlohmann::json::parse(SerString); + checkSer(PrintAsJsonSer, Gt); - LLVMAliasSet Deser(&IRDB, Ser); - checkDeser(*IRDB.getModule(), PTS, Deser); + LLVMAliasSet PrintAsJsonDeser(&IRDB, PrintAsJsonSer); + checkDeser(*IRDB.getModule(), PTS, PrintAsJsonDeser); } TEST(LLVMAliasSetSerializationTest, Ser_Intra01) { diff --git a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt index 512574c25..d4407ecef 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt +++ b/unittests/PhasarLLVM/TypeHierarchy/CMakeLists.txt @@ -1,5 +1,7 @@ set(PointerSources + DIBasedTypeHierarchySerializationTest.cpp DIBasedTypeHierarchyTest.cpp + LLVMTypeHierarchySerializationTest.cpp LLVMTypeHierarchyTest.cpp TypeGraphTest.cpp ) diff --git a/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchySerializationTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchySerializationTest.cpp new file mode 100644 index 000000000..72ea20f23 --- /dev/null +++ b/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchySerializationTest.cpp @@ -0,0 +1,104 @@ + +#include "phasar/Config/Configuration.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/StringRef.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +using namespace psr; + +/* ============== TEST FIXTURE ============== */ +class TypeHierarchySerialization + : public ::testing::TestWithParam { +protected: + static constexpr auto PathToLlFiles = + PHASAR_BUILD_SUBFOLDER("type_hierarchies/"); + const std::vector EntryPoints = {"main"}; + +}; // Test Fixture + +void compareResults(const psr::DIBasedTypeHierarchy &Orig, + const psr::DIBasedTypeHierarchy &Deser) { + + EXPECT_EQ(Orig.getAllTypes().size(), Deser.getAllTypes().size()); + EXPECT_EQ(Orig.getAllVTables().size(), Deser.getAllVTables().size()); + + for (const auto *OrigCurrentType : Orig.getAllTypes()) { + // check types + const auto *DeserTy = Deser.getType(Orig.getTypeName(OrigCurrentType)); + EXPECT_EQ(OrigCurrentType, DeserTy) + << "Failed to match type with name '" + << Orig.getTypeName(OrigCurrentType).str() << "'"; + + // check edges + const auto OrigSubTypes = Orig.subTypesOf(OrigCurrentType); + const auto DeserSubTypes = Deser.subTypesOf(OrigCurrentType); + + for (const auto &CurrOrigSubType : OrigSubTypes) { + bool DeserHasSubType = false; + + for (const auto &CurrDeserSubType : DeserSubTypes) { + if (CurrDeserSubType->getName() == CurrOrigSubType->getName()) { + DeserHasSubType = true; + break; + } + } + + EXPECT_TRUE(DeserHasSubType); + } + + // check virtual functions and vtables + if (OrigCurrentType != DeserTy) { + llvm::errs() << "Mismatched types:\n> OrigTy: " << *OrigCurrentType + << '\n'; + llvm::errs() << "> DeserTy: " << *DeserTy << '\n'; + } + } +} + +TEST_P(TypeHierarchySerialization, OrigAndDeserEqual) { + using namespace std::string_literals; + + psr::LLVMProjectIRDB IRDB(PathToLlFiles + GetParam()); + psr::DIBasedTypeHierarchy DIBTH(IRDB); + + std::string Ser; + llvm::raw_string_ostream StringStream(Ser); + + DIBTH.printAsJson(StringStream); + + psr::DIBasedTypeHierarchy DeserializedDIBTH( + &IRDB, psr::DIBasedTypeHierarchyData::loadJsonString(Ser)); + + compareResults(DIBTH, DeserializedDIBTH); +} + +static constexpr std::string_view TypeHierarchyTestFiles[] = { + "type_hierarchy_1_cpp_dbg.ll", "type_hierarchy_2_cpp_dbg.ll", + "type_hierarchy_3_cpp_dbg.ll", "type_hierarchy_4_cpp_dbg.ll", + "type_hierarchy_5_cpp_dbg.ll", "type_hierarchy_6_cpp_dbg.ll", + "type_hierarchy_7_cpp_dbg.ll", "type_hierarchy_7_b_cpp_dbg.ll", + "type_hierarchy_8_cpp_dbg.ll", "type_hierarchy_9_cpp_dbg.ll", + "type_hierarchy_10_cpp_dbg.ll", "type_hierarchy_11_cpp_dbg.ll", + "type_hierarchy_12_cpp_dbg.ll", "type_hierarchy_12_b_cpp_dbg.ll", + "type_hierarchy_12_c_cpp_dbg.ll", "type_hierarchy_13_cpp_dbg.ll", + "type_hierarchy_14_cpp_dbg.ll", "type_hierarchy_15_cpp_dbg.ll", + "type_hierarchy_16_cpp_dbg.ll", "type_hierarchy_17_cpp_dbg.ll", + "type_hierarchy_18_cpp_dbg.ll", "type_hierarchy_19_cpp_dbg.ll", + "type_hierarchy_20_cpp_dbg.ll", "type_hierarchy_21_cpp_dbg.ll", +}; + +INSTANTIATE_TEST_SUITE_P(TypeHierarchySerializationTest, + TypeHierarchySerialization, + ::testing::ValuesIn(TypeHierarchyTestFiles)); + +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + auto Res = RUN_ALL_TESTS(); + return Res; +} diff --git a/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyTest.cpp index 221816582..5e0c1f890 100644 --- a/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyTest.cpp +++ b/unittests/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchyTest.cpp @@ -24,9 +24,9 @@ TEST(DBTHTest, BasicTHReconstruction_1) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -44,9 +44,9 @@ TEST(DBTHTest, BasicTHReconstruction_2) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -64,9 +64,9 @@ TEST(DBTHTest, BasicTHReconstruction_3) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -84,9 +84,9 @@ TEST(DBTHTest, BasicTHReconstruction_4) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -104,11 +104,11 @@ TEST(DBTHTest, BasicTHReconstruction_5) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 3U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &OtherBaseType = DBTH.getType("OtherBase"); + const auto &OtherBaseType = DBTH.getType("_ZTS9OtherBase"); ASSERT_NE(nullptr, OtherBaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -129,9 +129,9 @@ TEST(DBTHTest, BasicTHReconstruction_6) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -149,19 +149,19 @@ TEST(DBTHTest, BasicTHReconstruction_7) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 7U); - const auto &AType = DBTH.getType("A"); + const auto &AType = DBTH.getType("_ZTS1A"); ASSERT_NE(nullptr, AType); - const auto &BType = DBTH.getType("B"); + const auto &BType = DBTH.getType("_ZTS1B"); ASSERT_NE(nullptr, BType); - const auto &CType = DBTH.getType("C"); + const auto &CType = DBTH.getType("_ZTS1C"); ASSERT_NE(nullptr, CType); - const auto &DType = DBTH.getType("D"); + const auto &DType = DBTH.getType("_ZTS1D"); ASSERT_NE(nullptr, DType); - const auto &XType = DBTH.getType("X"); + const auto &XType = DBTH.getType("_ZTS1X"); ASSERT_NE(nullptr, XType); - const auto &YType = DBTH.getType("Y"); + const auto &YType = DBTH.getType("_ZTS1Y"); ASSERT_NE(nullptr, YType); - const auto &ZType = DBTH.getType("Z"); + const auto &ZType = DBTH.getType("_ZTS1Z"); ASSERT_NE(nullptr, ZType); EXPECT_TRUE(DBTH.hasType(AType)); @@ -202,15 +202,15 @@ TEST(DBTHTest, BasicTHReconstruction_7_b) { EXPECT_EQ(DBTH.getAllTypes().size(), 6U); const auto &AType = DBTH.getType("A"); ASSERT_NE(nullptr, AType); - const auto &CType = DBTH.getType("C"); + const auto &CType = DBTH.getType("_ZTS1C"); ASSERT_NE(nullptr, CType); const auto &XType = DBTH.getType("X"); ASSERT_NE(nullptr, XType); - const auto &YType = DBTH.getType("Y"); + const auto &YType = DBTH.getType("_ZTS1Y"); ASSERT_NE(nullptr, YType); - const auto &ZType = DBTH.getType("Z"); + const auto &ZType = DBTH.getType("_ZTS1Z"); ASSERT_NE(nullptr, ZType); - const auto &OmegaType = DBTH.getType("Omega"); + const auto &OmegaType = DBTH.getType("_ZTS5Omega"); ASSERT_NE(nullptr, OmegaType); EXPECT_TRUE(DBTH.hasType(AType)); @@ -247,13 +247,13 @@ TEST(DBTHTest, BasicTHReconstruction_8) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 4U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &NonvirtualClassType = DBTH.getType("NonvirtualClass"); + const auto &NonvirtualClassType = DBTH.getType("_ZTS15NonvirtualClass"); EXPECT_NE(nullptr, NonvirtualClassType); - const auto &NonvirtualStructType = DBTH.getType("NonvirtualStruct"); + const auto &NonvirtualStructType = DBTH.getType("_ZTS16NonvirtualStruct"); EXPECT_NE(nullptr, NonvirtualStructType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -273,9 +273,9 @@ TEST(DBTHTest, BasicTHReconstruction_9) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -293,9 +293,9 @@ TEST(DBTHTest, BasicTHReconstruction_10) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -313,9 +313,9 @@ TEST(DBTHTest, BasicTHReconstruction_11) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -333,9 +333,9 @@ TEST(DBTHTest, BasicTHReconstruction_12) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 2U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -357,7 +357,7 @@ TEST(DBTHTest, BasicTHReconstruction_12_b) { ASSERT_NE(nullptr, BaseType); const auto &ChildType = DBTH.getType("Child"); ASSERT_NE(nullptr, ChildType); - const auto &ChildsChildType = DBTH.getType("ChildsChild"); + const auto &ChildsChildType = DBTH.getType("_ZTS11ChildsChild"); ASSERT_NE(nullptr, ChildsChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -380,7 +380,7 @@ TEST(DBTHTest, BasicTHReconstruction_12_c) { EXPECT_EQ(DBTH.getAllTypes().size(), 2U); const auto &ChildType = DBTH.getType("Child"); ASSERT_NE(nullptr, ChildType); - const auto &ChildsChildType = DBTH.getType("ChildsChild"); + const auto &ChildsChildType = DBTH.getType("_ZTS11ChildsChild"); ASSERT_NE(nullptr, ChildsChildType); EXPECT_TRUE(DBTH.hasType(ChildType)); @@ -439,16 +439,16 @@ TEST(DBTHTest, BasicTHReconstruction_16) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 5U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); // Since ChildsChild is never used, it is optimized out // const auto &ChildsChildType = DBTH.getType("ChildsChild"); // ASSERT_EQ(nullptr, ChildsChildType); - const auto &BaseTwoType = DBTH.getType("BaseTwo"); + const auto &BaseTwoType = DBTH.getType("_ZTS7BaseTwo"); ASSERT_NE(nullptr, BaseTwoType); - const auto &ChildTwoType = DBTH.getType("ChildTwo"); + const auto &ChildTwoType = DBTH.getType("_ZTS8ChildTwo"); ASSERT_NE(nullptr, ChildTwoType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -475,16 +475,16 @@ TEST(DBTHTest, BasicTHReconstruction_17) { // check for all types // EXPECT_EQ(DBTH.getAllTypes().size(), 5U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); // const auto &Child2Type = DBTH.getType("Child2"); // Since Child2Type is never used, it is optimized out // ASSERT_EQ(nullptr, Child2Type); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &KidType = DBTH.getType("Kid"); + const auto &KidType = DBTH.getType("_ZTS3Kid"); ASSERT_NE(nullptr, KidType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -511,14 +511,14 @@ TEST(DBTHTest, BasicTHReconstruction_18) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 4U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); // const auto &Child_2Type = DBTH.getType("Child_2"); // Since Child2Type is never used, it is optimized out // ASSERT_EQ(nullptr, Child2Type); - const auto &Child3Type = DBTH.getType("Child_3"); + const auto &Child3Type = DBTH.getType("_ZTS7Child_3"); ASSERT_NE(nullptr, Child3Type); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -544,17 +544,17 @@ TEST(DBTHTest, BasicTHReconstruction_19) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 6U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &FooType = DBTH.getType("Foo"); + const auto &FooType = DBTH.getType("_ZTS3Foo"); ASSERT_NE(nullptr, FooType); - const auto &BarType = DBTH.getType("Bar"); + const auto &BarType = DBTH.getType("_ZTS3Bar"); ASSERT_NE(nullptr, BarType); - const auto &LoremType = DBTH.getType("Lorem"); + const auto &LoremType = DBTH.getType("_ZTS5Lorem"); ASSERT_NE(nullptr, LoremType); - const auto &ImpsumType = DBTH.getType("Impsum"); + const auto &ImpsumType = DBTH.getType("_ZTS6Impsum"); ASSERT_NE(nullptr, ImpsumType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -580,11 +580,11 @@ TEST(DBTHTest, BasicTHReconstruction_20) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 3U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -605,15 +605,15 @@ TEST(DBTHTest, BasicTHReconstruction_21) { // check for all types EXPECT_EQ(DBTH.getAllTypes().size(), 5U); - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &Base3Type = DBTH.getType("Base3"); + const auto &Base3Type = DBTH.getType("_ZTS5Base3"); ASSERT_NE(nullptr, Base3Type); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &Child2Type = DBTH.getType("Child2"); + const auto &Child2Type = DBTH.getType("_ZTS6Child2"); ASSERT_NE(nullptr, Child2Type); EXPECT_TRUE(DBTH.hasType(BaseType)); @@ -645,9 +645,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_1) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -667,9 +667,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_2) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -689,9 +689,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_3) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -709,9 +709,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_4) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -731,11 +731,11 @@ TEST(DBTHTest, TransitivelyReachableTypes_5) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &OtherBaseType = DBTH.getType("OtherBase"); + const auto &OtherBaseType = DBTH.getType("_ZTS9OtherBase"); ASSERT_NE(nullptr, OtherBaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -760,9 +760,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_6) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -782,19 +782,19 @@ TEST(DBTHTest, TransitivelyReachableTypes_7) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &AType = DBTH.getType("A"); + const auto &AType = DBTH.getType("_ZTS1A"); ASSERT_NE(nullptr, AType); - const auto &BType = DBTH.getType("B"); + const auto &BType = DBTH.getType("_ZTS1B"); ASSERT_NE(nullptr, BType); - const auto &CType = DBTH.getType("C"); + const auto &CType = DBTH.getType("_ZTS1C"); ASSERT_NE(nullptr, CType); - const auto &DType = DBTH.getType("D"); + const auto &DType = DBTH.getType("_ZTS1D"); ASSERT_NE(nullptr, DType); - const auto &XType = DBTH.getType("X"); + const auto &XType = DBTH.getType("_ZTS1X"); ASSERT_NE(nullptr, XType); - const auto &YType = DBTH.getType("Y"); + const auto &YType = DBTH.getType("_ZTS1Y"); ASSERT_NE(nullptr, YType); - const auto &ZType = DBTH.getType("Z"); + const auto &ZType = DBTH.getType("_ZTS1Z"); ASSERT_NE(nullptr, ZType); auto ReachableTypesA = DBTH.getSubTypes(AType); @@ -878,15 +878,15 @@ TEST(DBTHTest, TransitivelyReachableTypes_7_b) { // check for all types const auto &AType = DBTH.getType("A"); ASSERT_NE(nullptr, AType); - const auto &CType = DBTH.getType("C"); + const auto &CType = DBTH.getType("_ZTS1C"); ASSERT_NE(nullptr, CType); const auto &XType = DBTH.getType("X"); ASSERT_NE(nullptr, XType); - const auto &YType = DBTH.getType("Y"); + const auto &YType = DBTH.getType("_ZTS1Y"); ASSERT_NE(nullptr, YType); - const auto &ZType = DBTH.getType("Z"); + const auto &ZType = DBTH.getType("_ZTS1Z"); ASSERT_NE(nullptr, ZType); - const auto &OmegaType = DBTH.getType("Omega"); + const auto &OmegaType = DBTH.getType("_ZTS5Omega"); ASSERT_NE(nullptr, OmegaType); auto ReachableTypesA = DBTH.getSubTypes(AType); @@ -952,13 +952,13 @@ TEST(DBTHTest, TransitivelyReachableTypes_8) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &NonvirtualClassType = DBTH.getType("NonvirtualClass"); + const auto &NonvirtualClassType = DBTH.getType("_ZTS15NonvirtualClass"); ASSERT_NE(nullptr, NonvirtualClassType); - const auto &NonvirtualStructType = DBTH.getType("NonvirtualStruct"); + const auto &NonvirtualStructType = DBTH.getType("_ZTS16NonvirtualStruct"); ASSERT_NE(nullptr, NonvirtualStructType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -998,9 +998,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_9) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1020,9 +1020,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_10) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1042,9 +1042,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_11) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1064,9 +1064,9 @@ TEST(DBTHTest, TransitivelyReachableTypes_12) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1090,7 +1090,7 @@ TEST(DBTHTest, TransitivelyReachableTypes_12_b) { ASSERT_NE(nullptr, BaseType); const auto &ChildType = DBTH.getType("Child"); ASSERT_NE(nullptr, ChildType); - const auto &ChildsChildType = DBTH.getType("ChildsChild"); + const auto &ChildsChildType = DBTH.getType("_ZTS11ChildsChild"); ASSERT_NE(nullptr, ChildsChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1119,7 +1119,7 @@ TEST(DBTHTest, TransitivelyReachableTypes_12_c) { // check for all types const auto &ChildType = DBTH.getType("Child"); ASSERT_NE(nullptr, ChildType); - const auto &ChildsChildType = DBTH.getType("ChildsChild"); + const auto &ChildsChildType = DBTH.getType("_ZTS11ChildsChild"); ASSERT_NE(nullptr, ChildsChildType); auto ReachableTypesChild = DBTH.getSubTypes(ChildType); @@ -1182,16 +1182,16 @@ TEST(DBTHTest, TransitivelyReachableTypes_16) { "type_hierarchies/type_hierarchy_16_cpp_dbg.ll"); DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); // const auto &ChildsChildType = DBTH.getType("ChildsChild"); // Since ChildsChild is never used, it is optimized out // ASSERT_EQ(nullptr, ChildsChildType); - const auto &BaseTwoType = DBTH.getType("BaseTwo"); + const auto &BaseTwoType = DBTH.getType("_ZTS7BaseTwo"); ASSERT_NE(nullptr, BaseTwoType); - const auto &ChildTwoType = DBTH.getType("ChildTwo"); + const auto &ChildTwoType = DBTH.getType("_ZTS8ChildTwo"); ASSERT_NE(nullptr, ChildTwoType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1227,16 +1227,16 @@ TEST(DBTHTest, TransitivelyReachableTypes_17) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); // const auto &Child2Type = DBTH.getType("Child2"); // Since Child2 is never used, it is optimized out // ASSERT_EQ(nullptr, Child2Type); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &KidType = DBTH.getType("Kid"); + const auto &KidType = DBTH.getType("_ZTS3Kid"); ASSERT_NE(nullptr, KidType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1272,13 +1272,13 @@ TEST(DBTHTest, TransitivelyReachableTypes_18) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &Child2Type = DBTH.getType("Child_2"); + const auto &Child2Type = DBTH.getType("_ZTS7Child_2"); ASSERT_NE(nullptr, Child2Type); - const auto &Child3Type = DBTH.getType("Child_3"); + const auto &Child3Type = DBTH.getType("_ZTS7Child_3"); ASSERT_NE(nullptr, Child3Type); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1310,17 +1310,17 @@ TEST(DBTHTest, TransitivelyReachableTypes_19) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &FooType = DBTH.getType("Foo"); + const auto &FooType = DBTH.getType("_ZTS3Foo"); ASSERT_NE(nullptr, FooType); - const auto &BarType = DBTH.getType("Bar"); + const auto &BarType = DBTH.getType("_ZTS3Bar"); ASSERT_NE(nullptr, BarType); - const auto &LoremType = DBTH.getType("Lorem"); + const auto &LoremType = DBTH.getType("_ZTS5Lorem"); ASSERT_NE(nullptr, LoremType); - const auto &ImpsumType = DBTH.getType("Impsum"); + const auto &ImpsumType = DBTH.getType("_ZTS6Impsum"); ASSERT_NE(nullptr, ImpsumType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1359,11 +1359,11 @@ TEST(DBTHTest, TransitivelyReachableTypes_20) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); @@ -1389,15 +1389,15 @@ TEST(DBTHTest, TransitivelyReachableTypes_21) { DIBasedTypeHierarchy DBTH(IRDB); // check for all types - const auto &BaseType = DBTH.getType("Base"); + const auto &BaseType = DBTH.getType("_ZTS4Base"); ASSERT_NE(nullptr, BaseType); - const auto &Base2Type = DBTH.getType("Base2"); + const auto &Base2Type = DBTH.getType("_ZTS5Base2"); ASSERT_NE(nullptr, Base2Type); - const auto &Base3Type = DBTH.getType("Base3"); + const auto &Base3Type = DBTH.getType("_ZTS5Base3"); ASSERT_NE(nullptr, Base3Type); - const auto &ChildType = DBTH.getType("Child"); + const auto &ChildType = DBTH.getType("_ZTS5Child"); ASSERT_NE(nullptr, ChildType); - const auto &Child2Type = DBTH.getType("Child2"); + const auto &Child2Type = DBTH.getType("_ZTS6Child2"); ASSERT_NE(nullptr, Child2Type); auto ReachableTypesBase = DBTH.getSubTypes(BaseType); diff --git a/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp new file mode 100644 index 000000000..8ce287caf --- /dev/null +++ b/unittests/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchySerializationTest.cpp @@ -0,0 +1,91 @@ + +#include "phasar/Config/Configuration.h" +#include "phasar/PhasarLLVM/DB/LLVMProjectIRDB.h" +#include "phasar/PhasarLLVM/TypeHierarchy/DIBasedTypeHierarchy.h" +#include "phasar/PhasarLLVM/TypeHierarchy/LLVMTypeHierarchy.h" +#include "phasar/PhasarLLVM/Utils/LLVMShorthands.h" +#include "phasar/Utils/NlohmannLogging.h" +#include "phasar/Utils/Utilities.h" + +#include "llvm/ADT/StringRef.h" +#include "llvm/IR/DerivedTypes.h" + +#include "TestConfig.h" +#include "gtest/gtest.h" + +using namespace psr; + +/* ============== TEST FIXTURE ============== */ +class LLVMTypeHierarchySerialization + : public ::testing::TestWithParam { +protected: + static constexpr auto PathToLlFiles = + PHASAR_BUILD_SUBFOLDER("type_hierarchies/"); + const std::vector EntryPoints = {"main"}; + +}; // Test Fixture + +void compareResults(psr::LLVMTypeHierarchy &Orig, + psr::LLVMTypeHierarchy &Deser) { + ASSERT_EQ(Orig.getAllTypes().size(), Deser.getAllTypes().size()); + + for (const auto &OrigCurrentType : Orig.getAllTypes()) { + // check types + EXPECT_EQ(OrigCurrentType, Deser.getType(OrigCurrentType->getName().str())); + + // check edges + for (const auto &OrigEdgeCurrentType : Orig.getAllTypes()) { + // Deser.isSubType can take the same arguments as Orig.isSubType, since + // Deser should have the same types + + bool ExpectedValue = Orig.isSubType(OrigCurrentType, OrigEdgeCurrentType); + bool DeserializedValue = + Deser.isSubType(Deser.getType(OrigCurrentType->getName().str()), + Deser.getType(OrigEdgeCurrentType->getName().str())); + + EXPECT_EQ(ExpectedValue, DeserializedValue); + } + } +} + +TEST_P(LLVMTypeHierarchySerialization, OrigAndDeserEqual) { + using namespace std::string_literals; + + psr::LLVMProjectIRDB IRDB(PathToLlFiles + GetParam()); + psr::LLVMTypeHierarchy TypeHierarchy(IRDB); + + std::string Ser; + llvm::raw_string_ostream StringStream(Ser); + + TypeHierarchy.printAsJson(StringStream); + + psr::LLVMTypeHierarchy DeserializedTypeHierarchy( + IRDB, psr::LLVMTypeHierarchyData::loadJsonString(Ser)); + + compareResults(TypeHierarchy, DeserializedTypeHierarchy); +} + +static constexpr std::string_view TypeHierarchyTestFiles[] = { + "type_hierarchy_1_cpp_dbg.ll", "type_hierarchy_2_cpp_dbg.ll", + "type_hierarchy_3_cpp_dbg.ll", "type_hierarchy_4_cpp_dbg.ll", + "type_hierarchy_5_cpp_dbg.ll", "type_hierarchy_6_cpp_dbg.ll", + "type_hierarchy_7_cpp_dbg.ll", "type_hierarchy_7_b_cpp_dbg.ll", + "type_hierarchy_8_cpp_dbg.ll", "type_hierarchy_9_cpp_dbg.ll", + "type_hierarchy_10_cpp_dbg.ll", "type_hierarchy_11_cpp_dbg.ll", + "type_hierarchy_12_cpp_dbg.ll", "type_hierarchy_12_b_cpp_dbg.ll", + "type_hierarchy_12_c_cpp_dbg.ll", "type_hierarchy_13_cpp_dbg.ll", + "type_hierarchy_14_cpp_dbg.ll", "type_hierarchy_15_cpp_dbg.ll", + "type_hierarchy_16_cpp_dbg.ll", "type_hierarchy_17_cpp_dbg.ll", + "type_hierarchy_18_cpp_dbg.ll", "type_hierarchy_19_cpp_dbg.ll", + "type_hierarchy_20_cpp_dbg.ll", "type_hierarchy_21_cpp_dbg.ll", +}; + +INSTANTIATE_TEST_SUITE_P(LLVMTypeHierarchySerializationTest, + LLVMTypeHierarchySerialization, + ::testing::ValuesIn(TypeHierarchyTestFiles)); + +int main(int Argc, char **Argv) { + ::testing::InitGoogleTest(&Argc, Argv); + auto Res = RUN_ALL_TESTS(); + return Res; +}