diff --git a/include/revng-c/Decompiler/DLALayouts.h b/include/revng-c/Decompiler/DLALayouts.h index 24776780c..7583e4624 100644 --- a/include/revng-c/Decompiler/DLALayouts.h +++ b/include/revng-c/Decompiler/DLALayouts.h @@ -257,14 +257,14 @@ using DeleteLayout = std::integral_constant; template -UniqueLayout makeUniqueLayout(Args &&... A) { +UniqueLayout makeUniqueLayout(Args &&...A) { return UniqueLayout(new T(std::forward(A)...), DeleteLayout()); } using LayoutVector = std::vector; template -Layout *createLayout(LayoutVector &S, Args &&... A) { +Layout *createLayout(LayoutVector &S, Args &&...A) { auto U = makeUniqueLayout(std::forward(A)...); return S.emplace_back(std::move(U)).get(); } @@ -280,7 +280,7 @@ public: explicit LayoutTypePtr(const llvm::Value *Val, unsigned Idx = fieldNumNone) : V(Val), FieldIdx(Idx) {} - LayoutTypePtr() = delete; + LayoutTypePtr() = default; ~LayoutTypePtr() = default; LayoutTypePtr(const LayoutTypePtr &) = default; LayoutTypePtr(LayoutTypePtr &&) = default; @@ -297,5 +297,6 @@ public: }; // end class LayoutTypePtr using ValueLayoutMap = std::map; +using LayoutTypePtrVect = std::vector; } // end namespace dla diff --git a/include/revng-c/Decompiler/DLAPass.h b/include/revng-c/Decompiler/DLAPass.h index 5c8d1f2b3..ee046037f 100644 --- a/include/revng-c/Decompiler/DLAPass.h +++ b/include/revng-c/Decompiler/DLAPass.h @@ -15,15 +15,15 @@ struct DLAPass : public llvm::ModulePass { static char ID; - DLAPass() : llvm::ModulePass(ID), Layouts(), ValueLayouts() {} + DLAPass() : llvm::ModulePass(ID), Layouts(), ValueLayoutsMap() {} bool runOnModule(llvm::Module &M) override; void getAnalysisUsage(llvm::AnalysisUsage &AU) const override; - const dla::ValueLayoutMap *getLayoutMap() const { return &ValueLayouts; } + const dla::ValueLayoutMap *getLayoutMap() const { return &ValueLayoutsMap; } private: dla::LayoutVector Layouts; - dla::ValueLayoutMap ValueLayouts; + dla::ValueLayoutMap ValueLayoutsMap; }; diff --git a/lib/Decompiler/ASTBuildAnalysis.cpp b/lib/Decompiler/ASTBuildAnalysis.cpp index 534504827..65d7603a8 100644 --- a/lib/Decompiler/ASTBuildAnalysis.cpp +++ b/lib/Decompiler/ASTBuildAnalysis.cpp @@ -34,7 +34,6 @@ #include "revng-c/Decompiler/DLALayouts.h" #include "ASTBuildAnalysis.h" - #include "AddSCEVBarrierPass.h" #include "DecompilationHelpers.h" #include "IRASTTypeTranslation.h" diff --git a/lib/Decompiler/CDecompilerAction.cpp b/lib/Decompiler/CDecompilerAction.cpp index e2a616109..d47cec3eb 100644 --- a/lib/Decompiler/CDecompilerAction.cpp +++ b/lib/Decompiler/CDecompilerAction.cpp @@ -22,9 +22,8 @@ #include "revng-c/RestructureCFGPass/ExprNode.h" #include "revng-c/RestructureCFGPass/RegionCFGTree.h" -#include "CDecompilerAction.h" - #include "ASTBuildAnalysis.h" +#include "CDecompilerAction.h" #include "DecompilationHelpers.h" #include "IRASTTypeTranslation.h" diff --git a/lib/Decompiler/CMakeLists.txt b/lib/Decompiler/CMakeLists.txt index 89d53f22e..5864626b0 100644 --- a/lib/Decompiler/CMakeLists.txt +++ b/lib/Decompiler/CMakeLists.txt @@ -29,7 +29,8 @@ revng_add_analyses_library(Decompiler revngc IRASTTypeTranslation.cpp MarkForSerialization.cpp SCEVBaseAddressExplorer.cpp - TypeDeclCreationAction.cpp) + TypeDeclCreationAction.cpp + DLATypeSystemBuilder.cpp) target_link_libraries(Decompiler FilterForDecompilation diff --git a/lib/Decompiler/DLACreateInterProceduralTypes.cpp b/lib/Decompiler/DLACreateInterProceduralTypes.cpp index afeea82d2..04172baa6 100644 --- a/lib/Decompiler/DLACreateInterProceduralTypes.cpp +++ b/lib/Decompiler/DLACreateInterProceduralTypes.cpp @@ -11,16 +11,13 @@ #include "revng/Support/FunctionTags.h" #include "revng/Support/IRHelpers.h" -#include "DLAStep.h" #include "DLATypeSystem.h" +#include "DLATypeSystemBuilder.h" using namespace dla; using namespace llvm; -using StepT = CreateInterproceduralTypes; - -bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { - const Module &M = TS.getModule(); +bool DLATypeSystemLLVMBuilder::createInterproceduralTypes(llvm::Module &M) { for (const Function &F : M.functions()) { auto FTags = FunctionTags::TagsSet::from(&F); if (F.isIntrinsic() or not FTags.contains(FunctionTags::Lifted)) @@ -28,14 +25,14 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { revng_assert(not F.isVarArg()); // Create the Function's return types - auto FRetTypes = TS.getOrCreateLayoutTypes(F); + auto FRetTypes = getOrCreateLayoutTypes(F); // Create types for the Function's arguments for (const Argument &Arg : F.args()) { // Arguments can only be integers and pointers revng_assert(isa(Arg.getType()) or isa(Arg.getType())); - auto N = TS.getOrCreateLayoutTypes(Arg).size(); + auto N = getOrCreateLayoutTypes(Arg).size(); // Given that arguments can only be integers or pointers, we should only // create a single LayoutType for each argument revng_assert(N == 1ULL); @@ -57,8 +54,8 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { or isa(ActualArg->getType())); revng_assert(isa(FormalArg.getType()) or isa(FormalArg.getType())); - auto ActualTypes = TS.getOrCreateLayoutTypes(*ActualArg); - auto FormalTypes = TS.getOrCreateLayoutTypes(FormalArg); + auto ActualTypes = getOrCreateLayoutTypes(*ActualArg); + auto FormalTypes = getOrCreateLayoutTypes(FormalArg); revng_assert(1ULL == ActualTypes.size() == FormalTypes.size()); auto FieldNum = FormalTypes.size(); for (auto FieldId = 0ULL; FieldId < FieldNum; ++FieldId) { @@ -72,12 +69,12 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { revng_assert(isa(PHI->getType()) or isa(PHI->getType()) or isa(PHI->getType())); - auto PHITypes = TS.getOrCreateLayoutTypes(*PHI); + auto PHITypes = getOrCreateLayoutTypes(*PHI); for (const Use &Incoming : PHI->incoming_values()) { revng_assert(isa(Incoming->getType()) or isa(Incoming->getType()) or isa(Incoming->getType())); - auto InTypes = TS.getOrCreateLayoutTypes(*Incoming.get()); + auto InTypes = getOrCreateLayoutTypes(*Incoming.get()); revng_assert(PHITypes.size() == InTypes.size()); revng_assert((PHITypes.size() == 1ULL) or isa(PHI->getType())); @@ -93,7 +90,7 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { revng_assert(isa(RetVal->getType()) or isa(RetVal->getType()) or isa(RetVal->getType())); - auto RetTypes = TS.getOrCreateLayoutTypes(*RetVal); + auto RetTypes = getOrCreateLayoutTypes(*RetVal); revng_assert(RetTypes.size() == FRetTypes.size()); auto FieldNum = RetTypes.size(); for (auto FieldId = 0ULL; FieldId < FieldNum; ++FieldId) { diff --git a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp index e0e549a8e..e25b7629c 100644 --- a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp +++ b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp @@ -19,19 +19,20 @@ #include "llvm/IR/Module.h" #include "llvm/Pass.h" +#include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "revng/Support/FunctionTags.h" #include "revng/Support/IRHelpers.h" #include "DLAHelpers.h" -#include "DLAStep.h" #include "DLATypeSystem.h" +#include "DLATypeSystemBuilder.h" #include "SCEVBaseAddressExplorer.h" using namespace dla; using namespace llvm; -using StepT = CreateIntraproceduralTypes; +static Logger<> AccessLog("dla-accesses-log"); // Returns true if an Instruction must forcibly be serialized. // @@ -54,7 +55,7 @@ static int64_t getSCEVConstantSExtVal(const SCEV *S) { return cast(S)->getAPInt().getSExtValue(); } -class InstanceLinkAdder { +class DLATypeSystemLLVMBuilder::InstanceLinkAdder { Function *F; ScalarEvolution *SE; llvm::DominatorTree DT; @@ -63,7 +64,7 @@ class InstanceLinkAdder { SCEVTypeMap SCEVToLayoutType; protected: - bool addInstanceLink(LayoutTypeSystem &TS, + bool addInstanceLink(DLATypeSystemLLVMBuilder &Builder, Value *PointerVal, const SCEV *BaseAddrSCEV, const BasicBlock &B) { @@ -89,7 +90,7 @@ protected: // create it and add it. Value *BaseAddr = U->getValue(); revng_assert(nullptr != BaseAddr); - const auto &[Layout, NewType] = TS.getOrCreateLayoutType(BaseAddr); + const auto &[Layout, NewType] = Builder.getOrCreateLayoutType(BaseAddr); Created |= NewType; auto P = std::make_pair(BaseAddrSCEV, Layout); Src = SCEVToLayoutType.emplace_hint(It, std::move(P))->second; @@ -101,7 +102,7 @@ protected: } revng_assert(Src != nullptr); - const auto &[Tgt, IsNewType] = TS.getOrCreateLayoutType(PointerVal); + const auto &[Tgt, IsNewType] = Builder.getOrCreateLayoutType(PointerVal); Created |= IsNewType; revng_assert(Tgt != nullptr); @@ -188,7 +189,7 @@ protected: if (OE.Offset < 0LL) return Created; - Created |= TS.addInstanceLink(Src, Tgt, std::move(OE)).second; + Created |= Builder.TS.addInstanceLink(Src, Tgt, std::move(OE)).second; return Created; } @@ -201,7 +202,7 @@ public: SCEVToLayoutType.clear(); } - bool getOrCreateSCEVTypes(LayoutTypeSystem &TS) { + bool getOrCreateSCEVTypes(DLATypeSystemLLVMBuilder &Builder) { bool Changed = false; // Add entry in SCEVToLayoutType map for arguments. We always add these @@ -209,11 +210,13 @@ public: for (Argument &A : F->args()) { revng_assert(isa(A.getType()) or isa(A.getType())); - LayoutTypeSystemNode *ArgLayout = TS.getLayoutType(&A); + LayoutTypeSystemNode *ArgLayout = Builder.getLayoutType(&A); const SCEV *S = SE->getSCEV(&A); SCEVToLayoutType.insert(std::make_pair(S, ArgLayout)); } + auto &TS = Builder.TS; + for (BasicBlock &B : *F) { for (auto &I : B) { // Add entry in SCEVToLayoutType map for values returned by F @@ -223,7 +226,7 @@ public: or isa(RetVal->getType()) or isa(RetVal->getType())); if (isa(RetVal->getType())) { - auto RetTys = TS.getLayoutTypes(*RetVal); + auto RetTys = Builder.getLayoutTypes(*RetVal); auto NRetTypes = RetTys.size(); revng_assert(NRetTypes > 1ULL); @@ -250,12 +253,13 @@ public: revng_assert(RetTy->getNumElements() == Callee->arg_size()); revng_assert(RetTy == F->getReturnType()); - auto StructTypeNodes = TS.getOrCreateLayoutTypes(*Call); + auto StructTypeNodes = Builder.getOrCreateLayoutTypes(*Call); revng_assert(StructTypeNodes.size() == Callee->arg_size()); for (const auto &[RetNodeNew, Arg] : llvm::zip_first(StructTypeNodes, Call->arg_operands())) { - const auto &[ArgNode, New] = TS.getOrCreateLayoutType(Arg); + const auto &[ArgNode, + New] = Builder.getOrCreateLayoutType(Arg); Changed |= New; const auto &[RetNode, NewNode] = RetNodeNew; Changed |= NewNode; @@ -276,7 +280,7 @@ public: SCEVToLayoutType.insert(std::make_pair(S, RetTys[N])); } } else { - LayoutTypeSystemNode *RetTy = TS.getLayoutType(RetVal); + LayoutTypeSystemNode *RetTy = Builder.getLayoutType(RetVal); const SCEV *S = SE->getSCEV(RetVal); SCEVToLayoutType.insert(std::make_pair(S, RetTy)); } @@ -292,7 +296,7 @@ public: or isa(PHI->getType())); if (not isa(PHI->getType())) { - LayoutTypeSystemNode *PHIType = TS.getLayoutType(PHI); + LayoutTypeSystemNode *PHIType = Builder.getLayoutType(PHI); const SCEV *PHISCEV = SE->getSCEV(PHI); SCEVToLayoutType.insert(std::make_pair(PHISCEV, PHIType)); @@ -300,7 +304,7 @@ public: for (Value *In : PHI->incoming_values()) { revng_assert(isa(In->getType()) or isa(In->getType())); - LayoutTypeSystemNode *InTy = TS.getLayoutType(In); + LayoutTypeSystemNode *InTy = Builder.getLayoutType(In); const SCEV *InSCEV = SE->getSCEV(In); SCEVToLayoutType.insert(std::make_pair(InSCEV, InTy)); } @@ -318,7 +322,7 @@ public: or isa(Sel->getType())); // Selects are very much like PHIs. - const auto &[SelType, New] = TS.getOrCreateLayoutType(Sel); + const auto &[SelType, New] = Builder.getOrCreateLayoutType(Sel); Changed |= New; const SCEV *SelSCEV = SE->getSCEV(Sel); SCEVToLayoutType.insert(std::make_pair(SelSCEV, SelType)); @@ -328,11 +332,11 @@ public: Value *TrueV = Sel->getTrueValue(); revng_assert(isa(TrueV->getType()) or isa(TrueV->getType())); - const auto &[TrueTy, NewT] = TS.getOrCreateLayoutType(TrueV); + const auto &[TrueTy, NewT] = Builder.getOrCreateLayoutType(TrueV); Changed |= NewT; const SCEV *TrueSCEV = SE->getSCEV(TrueV); SCEVToLayoutType.insert(std::make_pair(TrueSCEV, TrueTy)); - Changed |= TS.addInheritanceLink(TrueTy, SelType).second; + Changed |= Builder.TS.addInheritanceLink(TrueTy, SelType).second; } // False incoming value @@ -340,11 +344,11 @@ public: Value *FalseV = Sel->getFalseValue(); revng_assert(isa(FalseV->getType()) or isa(FalseV->getType())); - const auto &[FalseTy, NewT] = TS.getOrCreateLayoutType(FalseV); + const auto &[FalseTy, NewT] = Builder.getOrCreateLayoutType(FalseV); Changed |= NewT; const SCEV *FalseSCEV = SE->getSCEV(FalseV); SCEVToLayoutType.insert(std::make_pair(FalseSCEV, FalseTy)); - Changed |= TS.addInheritanceLink(FalseTy, SelType).second; + Changed |= Builder.TS.addInheritanceLink(FalseTy, SelType).second; } } else if (auto *C = dyn_cast(&I)) { @@ -356,7 +360,7 @@ public: if (Callee->hasName() and Callee->getName() == "revng_init_local_sp") { - const auto &[StackLayout, New] = TS.getOrCreateLayoutType(C); + const auto &[StackLayout, New] = Builder.getOrCreateLayoutType(C); Changed |= New; const SCEV *CallSCEV = SE->getSCEV(C); SCEVToLayoutType.insert(std::make_pair(CallSCEV, StackLayout)); @@ -371,12 +375,13 @@ public: auto *RetTy = cast(Callee->getReturnType()); revng_assert(RetTy->getNumElements() == Callee->arg_size()); - auto StructTypeNodes = TS.getOrCreateLayoutTypes(*C); + auto StructTypeNodes = Builder.getOrCreateLayoutTypes(*C); revng_assert(StructTypeNodes.size() == Callee->arg_size()); for (const auto &[RetTypeNodeNew, Arg] : llvm::zip_first(StructTypeNodes, C->arg_operands())) { - const auto &[ArgTypeNode, New] = TS.getOrCreateLayoutType(Arg); + const auto &[ArgTypeNode, + New] = Builder.getOrCreateLayoutType(Arg); Changed |= New; const auto &[RetTypeNode, NewNode] = RetTypeNodeNew; Changed |= NewNode; @@ -415,7 +420,7 @@ public: if (isa(C->getType())) { // Types representing the return type - auto FormalRetTys = TS.getLayoutTypes(*Callee); + auto FormalRetTys = Builder.getLayoutTypes(*Callee); auto Size = FormalRetTys.size(); auto ExtractedVals = getExtractedValuesFromInstruction(C); revng_assert(Size == ExtractedVals.size()); @@ -431,7 +436,8 @@ public: revng_assert(isa(ExtTy) or isa(ExtTy)); - const auto &[ExtLayout, New] = TS.getOrCreateLayoutType(E); + const auto &[ExtLayout, + New] = Builder.getOrCreateLayoutType(E); Changed |= New; Changed |= TS.addEqualityLink(RetTy, ExtLayout).second; const SCEV *S = SE->getSCEV(E); @@ -441,10 +447,10 @@ public: } else { // Type representing the return type revng_assert(not C->getType()->isIntegerTy(1)); - LayoutTypeSystemNode *RetTy = TS.getLayoutType(Callee); - const auto &[CType, NewC] = TS.getOrCreateLayoutType(C); + LayoutTypeSystemNode *RetTy = Builder.getLayoutType(Callee); + const auto &[CType, NewC] = Builder.getOrCreateLayoutType(C); Changed |= NewC; - Changed |= TS.addEqualityLink(RetTy, CType).second; + Changed |= Builder.TS.addEqualityLink(RetTy, CType).second; const SCEV *RetS = SE->getSCEV(C); SCEVToLayoutType.insert(std::make_pair(RetS, CType)); } @@ -454,7 +460,7 @@ public: for (Use &ArgU : C->arg_operands()) { revng_assert(isa(ArgU->getType()) or isa(ArgU->getType())); - const auto &[ArgTy, Created] = TS.getOrCreateLayoutType(ArgU); + const auto &[ArgTy, Created] = Builder.getOrCreateLayoutType(ArgU); Changed |= Created; const SCEV *ArgS = SE->getSCEV(ArgU); SCEVToLayoutType.insert(std::make_pair(ArgS, ArgTy)); @@ -488,9 +494,10 @@ public: LayoutTypeSystemNode *SrcLayout = nullptr; LayoutTypeSystemNode *TgtLayout = nullptr; - std::tie(SrcLayout, New) = TS.getOrCreateLayoutType(Op); + std::tie(SrcLayout, New) = Builder.getOrCreateLayoutType(Op); Changed |= New; - std::tie(TgtLayout, New) = TS.getOrCreateLayoutType(CExpr); + std::tie(TgtLayout, + New) = Builder.getOrCreateLayoutType(CExpr); Changed |= New; Changed |= TS.addEqualityLink(SrcLayout, TgtLayout).second; @@ -507,7 +514,7 @@ public: or isa(L->getType())); revng_assert(not L->getType()->isIntegerTy(1)); - const auto &[LoadedTy, Created] = TS.getOrCreateLayoutType(L); + const auto &[LoadedTy, Created] = Builder.getOrCreateLayoutType(L); Changed |= Created; const SCEV *LoadSCEV = SE->getSCEV(L); SCEVToLayoutType.insert(std::make_pair(LoadSCEV, LoadedTy)); @@ -515,7 +522,7 @@ public: } else if (auto *A = dyn_cast(&I)) { revng_assert(isa(A->getType()->getElementType()) or isa(A->getType()->getElementType())); - const auto &[LoadedTy, Created] = TS.getOrCreateLayoutType(A); + const auto &[LoadedTy, Created] = Builder.getOrCreateLayoutType(A); Changed |= Created; const SCEV *LoadSCEV = SE->getSCEV(A); SCEVToLayoutType.insert(std::make_pair(LoadSCEV, LoadedTy)); @@ -527,12 +534,12 @@ public: LayoutTypeSystemNode *SrcLayout = nullptr; LayoutTypeSystemNode *TgtLayout = nullptr; - std::tie(SrcLayout, New) = TS.getOrCreateLayoutType(Op); + std::tie(SrcLayout, New) = Builder.getOrCreateLayoutType(Op); Changed |= New; - std::tie(TgtLayout, New) = TS.getOrCreateLayoutType(&I); + std::tie(TgtLayout, New) = Builder.getOrCreateLayoutType(&I); Changed |= New; - Changed |= TS.addEqualityLink(SrcLayout, TgtLayout).second; + Changed |= Builder.TS.addEqualityLink(SrcLayout, TgtLayout).second; const SCEV *LoadSCEV = SE->getSCEV(&I); SCEVToLayoutType.insert(std::make_pair(LoadSCEV, TgtLayout)); } @@ -541,7 +548,7 @@ public: return Changed; } - bool createBaseAddrWithInstanceLink(LayoutTypeSystem &TS, + bool createBaseAddrWithInstanceLink(DLATypeSystemLLVMBuilder &Builder, Value *PointerVal, const BasicBlock &B) { revng_assert(PointerVal); @@ -558,24 +565,41 @@ public: PtrSCEV, SCEVToLayoutType); for (const SCEV *BaseAddrSCEV : PossibleBaseAddresses) - AddedSomething |= addInstanceLink(TS, PointerVal, BaseAddrSCEV, B); + AddedSomething |= addInstanceLink(Builder, PointerVal, BaseAddrSCEV, B); return AddedSomething; } }; -bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { +using Builder = DLATypeSystemLLVMBuilder; +bool Builder::createIntraproceduralTypes(llvm::Module &M, + llvm::ModulePass *MP) { bool Changed = false; InstanceLinkAdder ILA; - Module &M = TS.getModule(); + + raw_fd_ostream *OutFile = nullptr; + + if (AccessLog.isEnabled()) { + std::error_code EC; + OutFile = new raw_fd_ostream("DLA_pointer_accesses.csv", EC); + revng_check(not EC, "Cannot open DLA_pointer_accesses.csv"); + + (*OutFile) << "Value" + << ";" + << "Access Size" + << ";" + << "Accessed By" + << "\n"; + } + for (Function &F : M.functions()) { auto FTags = FunctionTags::TagsSet::from(&F); if (F.isIntrinsic() or not FTags.contains(FunctionTags::Lifted)) continue; revng_assert(not F.isVarArg()); - ILA.setupForProcessingFunction(ModPass, &F); - Changed |= ILA.getOrCreateSCEVTypes(TS); + ILA.setupForProcessingFunction(MP, &F); + Changed |= ILA.getOrCreateSCEVTypes(*this); llvm::ReversePostOrderTraversal RPOT(&F.getEntryBlock()); for (BasicBlock *B : RPOT) { @@ -631,10 +655,16 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { continue; } - Changed |= ILA.createBaseAddrWithInstanceLink(TS, PointerVal, *B); - auto *AddrLayout = TS.getLayoutType(PointerVal); - auto AccessSize = getLoadStoreSizeFromPtrOpUse(TS, PtrUse); + Changed |= ILA.createBaseAddrWithInstanceLink(*this, PointerVal, *B); + auto *AddrLayout = getLayoutType(PointerVal); + auto AccessSize = getLoadStoreSizeFromPtrOpUse(M, PtrUse); AddrLayout->AccessSizes.insert(AccessSize); + + if (AccessLog.isEnabled()) { + revng_assert(OutFile); + (*OutFile) << *PointerVal << ";" << AccessSize << ";" << I << "\n"; + } + continue; } @@ -731,7 +761,9 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { for (Value *PointerVal : Pointers) { if (PointerVal and not isa(PointerVal->getType())) - Changed |= ILA.createBaseAddrWithInstanceLink(TS, PointerVal, *B); + Changed |= ILA.createBaseAddrWithInstanceLink(*this, + PointerVal, + *B); } } } diff --git a/lib/Decompiler/DLAHelpers.cpp b/lib/Decompiler/DLAHelpers.cpp index e04f7f4d0..47fbb1bb7 100644 --- a/lib/Decompiler/DLAHelpers.cpp +++ b/lib/Decompiler/DLAHelpers.cpp @@ -21,7 +21,6 @@ #include "revng/Support/Debug.h" #include "DLAHelpers.h" - #include "DLATypeSystem.h" template @@ -167,12 +166,12 @@ getExtractedValuesFromInstruction(const llvm::Instruction *I) { return getConstQualifiedExtractedValuesFromInstruction(I); } -uint64_t getLoadStoreSizeFromPtrOpUse(const dla::LayoutTypeSystem &TS, - const llvm::Use *U) { +uint64_t +getLoadStoreSizeFromPtrOpUse(const llvm::Module &M, const llvm::Use *U) { llvm::Value *AddrOperand = U->get(); auto *PtrTy = cast(AddrOperand->getType()); llvm::Type *AccessedT = PtrTy->getElementType(); - const llvm::DataLayout &DL = TS.getModule().getDataLayout(); + const llvm::DataLayout &DL = M.getDataLayout(); return DL.getTypeAllocSize(AccessedT); }; diff --git a/lib/Decompiler/DLAHelpers.h b/lib/Decompiler/DLAHelpers.h index fb73dd2d5..0136e231f 100644 --- a/lib/Decompiler/DLAHelpers.h +++ b/lib/Decompiler/DLAHelpers.h @@ -6,6 +6,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/IR/Module.h" namespace llvm { @@ -38,5 +39,5 @@ bool removeInstanceBackedgesFromInheritanceLoops(LayoutTypeSystem &TS); } // end namespace dla -uint64_t getLoadStoreSizeFromPtrOpUse(const dla::LayoutTypeSystem &TS, - const llvm::Use *U); +uint64_t +getLoadStoreSizeFromPtrOpUse(const llvm::Module &M, const llvm::Use *U); diff --git a/lib/Decompiler/DLAMakeLayouts.cpp b/lib/Decompiler/DLAMakeLayouts.cpp index 291edc762..5884e9f6e 100644 --- a/lib/Decompiler/DLAMakeLayouts.cpp +++ b/lib/Decompiler/DLAMakeLayouts.cpp @@ -19,6 +19,7 @@ #include "llvm/Support/raw_ostream.h" #include "revng/ADT/FilteredGraphTraits.h" +#include "revng/Support/Assert.h" #include "revng/Support/Debug.h" #include "revng-c/Decompiler/DLALayouts.h" @@ -39,7 +40,6 @@ static Layout *makeInstanceChildLayout(Layout *ChildType, const OffsetExpression &OE, LayoutVector &Layouts) { revng_assert(OE.Offset >= 0LL); - LayoutVector NewLayouts; // If we have trip counts we have an array of children of type ChildType, // otherwise ChildType already points to the right child type. @@ -48,7 +48,7 @@ static Layout *makeInstanceChildLayout(Layout *ChildType, Layout *Inner = ChildType; for (const auto &[TC, S] : llvm::zip(OE.TripCounts, OE.Strides)) { revng_assert(S > 0LL); - Layout::layout_size_t StrideSize = (Layout::layout_size_t)(S); + Layout::layout_size_t StrideSize = (Layout::layout_size_t) (S); // For now, we don't handle stuff that for which the size of the element // is larger than the stride size @@ -62,13 +62,13 @@ static Layout *makeInstanceChildLayout(Layout *ChildType, StructLayout::fields_container_t StructFields; StructFields.push_back(Inner); Layout::layout_size_t PadSize = StrideSize - Inner->size(); - Layout *Padding = createLayout(NewLayouts, PadSize); + Layout *Padding = createLayout(Layouts, PadSize); StructFields.push_back(Padding); - Inner = createLayout(NewLayouts, std::move(StructFields)); + Inner = createLayout(Layouts, std::move(StructFields)); } // Create the real array of Inner elements. - Inner = createLayout(NewLayouts, Inner, S, TC); + Inner = createLayout(Layouts, Inner, S, TC); } ChildType = Inner; } @@ -80,27 +80,34 @@ static Layout *makeInstanceChildLayout(Layout *ChildType, ArrayLayout::length_t Len = OE.Offset; // Create the struct with the padding prepended to the field. StructLayout::fields_container_t StructFields; - StructFields.push_back(createLayout(NewLayouts, Len)); + StructFields.push_back(createLayout(Layouts, Len)); StructFields.push_back(ChildType); - ChildType = createLayout(NewLayouts, std::move(StructFields)); + ChildType = createLayout(Layouts, std::move(StructFields)); } revng_assert(nullptr != ChildType); - Layouts.reserve(Layouts.size() + NewLayouts.size()); - for (auto &U : NewLayouts) - Layouts.push_back(std::move(U)); - return ChildType; } +static Layout *getLayout(const LayoutTypeSystem &TS, + LayoutPtrVector &OrderedLayouts, + const LTSN *N) { + // First, find the node's equivalence class ID + auto EqClassID = TS.getEqClasses().getEqClassID(N->ID); + if (not EqClassID) + return nullptr; + + revng_assert(*EqClassID < OrderedLayouts.size()); + // Get the layout at that position + Layout *L = OrderedLayouts[*EqClassID]; + revng_assert(L); + return L; +} + static Layout *makeLayout(const LayoutTypeSystem &TS, const LTSN *N, - std::map &LayoutCTypes, - LayoutVector &Layouts) { - - revng_assert(not LayoutCTypes.count(N)); - LayoutVector NewLayouts; - + LayoutVector &Layouts, + LayoutPtrVector &OrderedLayouts) { switch (N->InterferingInfo) { case AllChildrenAreNonInterfering: { @@ -206,14 +213,16 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, } } + // Create a BaseLayout as a first element of the struct revng_assert(not NumAccesses or NumAccesses == 1ULL); if (AccessSize) { - Layout *AccessLayout = createLayout(NewLayouts, AccessSize); + Layout *AccessLayout = createLayout(Layouts, AccessSize); SFlds.push_back(AccessLayout); } bool First = true; + // For each member of the struct for (const auto &OrdChild : Children) { const auto &[StartByte, Size, Child] = OrdChild; First = false; @@ -222,18 +231,19 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, revng_assert(Start >= AccessSize); auto PadSize = Start - AccessSize; // always >= 0; revng_assert(PadSize >= 0); + + // If an unaccessed layout is known to exist, add it as padding if (PadSize) { - Layout *Padding = createLayout(NewLayouts, PadSize); + Layout *Padding = createLayout(Layouts, PadSize); SFlds.push_back(Padding); } AccessSize = Start + Size; - revng_assert(LayoutCTypes.find(Child) != LayoutCTypes.end()); - Layout *ChildType = LayoutCTypes.at(Child); + Layout *ChildType = getLayout(TS, OrderedLayouts, Child); // Bail out if we have not constructed a union field, because it means // that this is not a supported case yet. - revng_assert(nullptr != ChildType); + revng_assert(ChildType); SFlds.push_back(ChildType); } @@ -244,15 +254,9 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, return nullptr; Layout *CreatedLayout = (SFlds.size() > 1ULL) ? - createLayout(NewLayouts, SFlds) : + createLayout(Layouts, SFlds) : *SFlds.begin(); - LayoutCTypes[N] = CreatedLayout; - - Layouts.reserve(Layouts.size() + NewLayouts.size()); - for (auto &U : NewLayouts) - Layouts.push_back(std::move(U)); - return CreatedLayout; } break; @@ -274,10 +278,8 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, // Ignore children for which we haven't created a layout, because they // only have children from which it was not possible to create valid // layouts. - auto ChildLayoutIt = LayoutCTypes.find(Child); - revng_assert(ChildLayoutIt != LayoutCTypes.end()); - - Layout *ChildType = ChildLayoutIt->second; + Layout *ChildType = getLayout(TS, OrderedLayouts, Child); + revng_assert(ChildType); switch (EdgeTag->getKind()) { @@ -285,7 +287,7 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, revng_log(Log, "Instance"); const OffsetExpression &OE = EdgeTag->getOffsetExpr(); revng_log(Log, "Has Offset: " << OE.Offset); - ChildType = makeInstanceChildLayout(ChildType, OE, NewLayouts); + ChildType = makeInstanceChildLayout(ChildType, OE, Layouts); } break; case TypeLinkTag::LK_Inheritance: { @@ -312,14 +314,8 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, return nullptr; Layout *CreatedLayout = (UFlds.size() > 1ULL) ? - createLayout(NewLayouts, UFlds) : + createLayout(Layouts, UFlds) : *UFlds.begin(); - - LayoutCTypes[N] = CreatedLayout; - - Layouts.reserve(Layouts.size() + NewLayouts.size()); - for (auto &U : NewLayouts) - Layouts.push_back(std::move(U)); return CreatedLayout; } break; @@ -330,14 +326,20 @@ static Layout *makeLayout(const LayoutTypeSystem &TS, return nullptr; } -static bool makeLayouts(const LayoutTypeSystem &TS, - LayoutVector &Layouts, - ValueLayoutMap &ValueLayouts) { +LayoutPtrVector makeLayouts(const LayoutTypeSystem &TS, LayoutVector &Layouts) { + if (Log.isEnabled()) + TS.dumpDotOnFile("final.dot"); + if (VerifyLog.isEnabled()) revng_assert(TS.verifyDAG() and TS.verifyInheritanceTree()); - std::map LayoutCTypes; + // Prepare the vector of layouts that correspond to actual LayoutTypePtrs + LayoutPtrVector OrderedLayouts; + OrderedLayouts.resize(TS.getEqClasses().getNumClasses()); + std::set Visited; + + // Create Layouts for (LTSN *Root : llvm::nodes(&TS)) { revng_assert(Root != nullptr); if (not isRoot(Root)) @@ -347,11 +349,17 @@ static bool makeLayouts(const LayoutTypeSystem &TS, // Leaves need to have ValidLayouts, otherwise they should have been // trimmed by PruneLayoutNodesWithoutLayout revng_assert(not isLeaf(N) or hasValidLayout(N)); - Layout *LN = makeLayout(TS, N, LayoutCTypes, Layouts); + Layout *LN = makeLayout(TS, N, Layouts, OrderedLayouts); if (nullptr == LN) { revng_log(Log, "Node ID: " << N->ID << " Type: Empty"); continue; } + + // Insert the layout at the index corresponding to the node's eq. class + auto LayoutIdx = TS.getEqClasses().getEqClassID(N->ID); + revng_assert(LayoutIdx); + OrderedLayouts[*LayoutIdx] = LN; + if (Log.isEnabled()) { llvm::dbgs() << "\nNode ID: " << N->ID << " Type: "; Layout::printText(llvm::dbgs(), LN); @@ -359,21 +367,24 @@ static bool makeLayouts(const LayoutTypeSystem &TS, Layout::printGraphic(llvm::dbgs(), LN); llvm::dbgs() << '\n'; } - if (auto *TypePtrs = TS.getLayoutTypePtrs(N)) { - for (const auto &Value : *TypePtrs) { - bool New = ValueLayouts.insert(std::make_pair(Value, LN)).second; - revng_assert(New); - } - } } } - return true; + + return OrderedLayouts; }; -bool MakeLayouts::runOnTypeSystem(LayoutTypeSystem &TS) { - if (Log.isEnabled()) - TS.dumpDotOnFile("final.dot"); - return makeLayouts(TS, Layouts, ValueLayouts); -} +ValueLayoutMap makeLayoutMap(const LayoutTypePtrVect &Values, + const LayoutPtrVector &Layouts, + const VectEqClasses &EqClasses) { + ValueLayoutMap ValMap; + for (size_t I = 0; I < Values.size(); I++) { + // The layout of the I-th Value is stored at the EqClass(I) index + auto LayoutIdx = EqClasses.getEqClassID(I); + if (LayoutIdx) + ValMap.insert(std::make_pair(Values[I], Layouts[*LayoutIdx])); + } + + return ValMap; +} } // end namespace dla diff --git a/lib/Decompiler/DLAPass.cpp b/lib/Decompiler/DLAPass.cpp index 8a188a08f..b2d8736f4 100644 --- a/lib/Decompiler/DLAPass.cpp +++ b/lib/Decompiler/DLAPass.cpp @@ -4,13 +4,17 @@ #include "revng/Model/LoadModelPass.h" +#include "revng-c/Decompiler/DLALayouts.h" #include "revng-c/Decompiler/DLAPass.h" #include "DLAStep.h" #include "DLATypeSystem.h" +#include "DLATypeSystemBuilder.h" char DLAPass::ID = 0; +static Logger<> BuilderLog("dla-builder-log"); + using Register = llvm::RegisterPass; static Register X("dla", "Data Layout Analysis Pass", false, false); @@ -22,12 +26,17 @@ void DLAPass::getAnalysisUsage(llvm::AnalysisUsage &AU) const { } bool DLAPass::runOnModule(llvm::Module &M) { - dla::StepManager SM; + dla::LayoutTypeSystem TS; - // Front-end Steps, that create initial nodes and edges - revng_check(SM.addStep(this)); - revng_check(SM.addStep(this)); - // Middle-end Steps, that manipulate nodes and edges + // Front-end: Create the LayoutTypeSystem graph from an LLVM module + dla::DLATypeSystemLLVMBuilder Builder{ TS }; + Builder.buildFromLLVMModule(M, this); + + if (BuilderLog.isEnabled()) + Builder.dumpValuesMapping("DLA-values-initial.csv"); + + // Middle-end Steps: manipulate nodes and edges of the DLATypeSystem graph + dla::StepManager SM; revng_check(SM.addStep()); revng_check(SM.addStep()); revng_check(SM.addStep()); @@ -35,13 +44,23 @@ bool DLAPass::runOnModule(llvm::Module &M) { revng_check(SM.addStep()); revng_check(SM.addStep()); revng_check(SM.addStep()); - // Back-end Steps, that build Layouts from LayoutTypeSystem nodes revng_check(SM.addStep()); - revng_check(SM.addStep(Layouts, ValueLayouts)); - - dla::LayoutTypeSystem TS(M); SM.run(TS); + if (BuilderLog.isEnabled()) + Builder.dumpValuesMapping("DLA-values-after-ME.csv"); + + // Compress the equivalence classes obtained after graph manipulation + dla::VectEqClasses &EqClasses = TS.getEqClasses(); + EqClasses.compress(); + + // Create Layouts from the final nodes of the graph + dla::LayoutPtrVector OrderedLayouts = makeLayouts(TS, this->Layouts); + + // Map Layouts back to their corresponding LayoutTypePtr + dla::LayoutTypePtrVect Values = Builder.getValues(); + this->ValueLayoutsMap = makeLayoutMap(Values, OrderedLayouts, EqClasses); + return true; } diff --git a/lib/Decompiler/DLAStep.cpp b/lib/Decompiler/DLAStep.cpp index 9cd64ff16..8ef46fece 100644 --- a/lib/Decompiler/DLAStep.cpp +++ b/lib/Decompiler/DLAStep.cpp @@ -9,8 +9,6 @@ namespace dla { -const char CreateInterproceduralTypes::ID = 0; -const char CreateIntraproceduralTypes::ID = 0; const char CollapseIdentityAndInheritanceCC::ID = 0; const char RemoveTransitiveInheritanceEdges::ID = 0; const char MakeInheritanceTree::ID = 0; @@ -19,7 +17,6 @@ const char ComputeUpperMemberAccesses::ID = 0; const char CollapseCompatibleArrays::ID = 0; const char PropagateInheritanceToAccessors::ID = 0; const char ComputeNonInterferingComponents::ID = 0; -const char MakeLayouts::ID = 0; static Logger<> DLAStepManagerLog("dla-step-manager"); diff --git a/lib/Decompiler/DLAStep.h b/lib/Decompiler/DLAStep.h index 601d8f7b8..0afb80e1d 100644 --- a/lib/Decompiler/DLAStep.h +++ b/lib/Decompiler/DLAStep.h @@ -14,11 +14,7 @@ #include "revng-c/Decompiler/DLALayouts.h" -namespace llvm { - -class ModulePass; - -} // end namespace llvm +#include "DLATypeSystem.h" namespace dla { @@ -61,37 +57,6 @@ public: const void *getStepID() const { return StepID; }; }; -/// dla::Step that creates types for Function's return types and fromal args -class CreateInterproceduralTypes : public Step { - static const char ID; - -public: - static const constexpr void *getID() { return &ID; } - - CreateInterproceduralTypes(llvm::ModulePass *MPass) : Step(ID){}; - - virtual ~CreateInterproceduralTypes() override = default; - - virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override; -}; - -/// dla::Step that creates types for LLVM Values inside Functions and edges -/// between them. -class CreateIntraproceduralTypes : public Step { - static const char ID; - llvm::ModulePass *ModPass; - -public: - static const constexpr void *getID() { return &ID; } - - CreateIntraproceduralTypes(llvm::ModulePass *MPass) : - Step(ID), ModPass(MPass){}; - - virtual ~CreateIntraproceduralTypes() override = default; - - virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override; -}; - /// dla::Step that collapses loops in the type system with equality or /// inheritange edges // @@ -242,31 +207,31 @@ public: virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override; }; -/// Final dla::Step, which flattens out the types into memory layouts -class MakeLayouts : public Step { - static const char ID; +/// Final step, which flattens out the types into memory layouts +using LayoutPtrVector = std::vector; -public: - static const constexpr void *getID() { return &ID; } +///\brief Generate Layout objects from a DLATypeSystem +/// +/// Some nodes of a DLATypeSystem graph can generate a Layout, that is added to +/// \a Layouts. +/// The returned vector stores pointers to the generated layouts in a specific +/// order: the index of a Layout in the vector is equal to the Node's ID +/// equivalence class. +/// Note that pointers in the returned vector may be duplicated. +/// +///\param[in] TS The graph that represents layouts and their relations +///\param[out] Layouts Where to put the constructed layouts +///\return a vector of Layouts ordered using TS equivalence classes +LayoutPtrVector makeLayouts(const LayoutTypeSystem &TS, LayoutVector &Layouts); - MakeLayouts(LayoutVector &L, ValueLayoutMap &M) : - Step(ID, - // Dependencies - { CollapseIdentityAndInheritanceCC::getID(), - RemoveTransitiveInheritanceEdges::getID() }, - // Invalidated - {}), - Layouts(L), - ValueLayouts(M) {} - - virtual ~MakeLayouts() override = default; - - virtual bool runOnTypeSystem(LayoutTypeSystem &TS) override; - -private: - LayoutVector &Layouts; - ValueLayoutMap &ValueLayouts; -}; +///\brief Create a map between LayoutTypePtrs and Layouts +///\param Values the list of LayoutTypePtrs +///\param OrderedLayouts the list of Layouts +///\param EqClasses equivalence classes between indexes of \a Values and +/// indexes of \a OrderedLayouts +ValueLayoutMap makeLayoutMap(const LayoutTypePtrVect &Values, + const LayoutPtrVector &OrderedLayouts, + const VectEqClasses &EqClasses); template bool intersect(IterT I1, IterT E1, IterT I2, IterT E2) { @@ -310,7 +275,7 @@ public: [[nodiscard]] bool addStep(std::unique_ptr S); template - [[nodiscard]] bool addStep(ArgsT &&... Args) { + [[nodiscard]] bool addStep(ArgsT &&...Args) { return addStep(std::make_unique(std::forward(Args)...)); } diff --git a/lib/Decompiler/DLATypeSystem.cpp b/lib/Decompiler/DLATypeSystem.cpp index 668ea21c4..02a48b3c5 100644 --- a/lib/Decompiler/DLATypeSystem.cpp +++ b/lib/Decompiler/DLATypeSystem.cpp @@ -8,19 +8,15 @@ #include "llvm/ADT/SCCIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" -#include "llvm/IR/Argument.h" -#include "llvm/IR/Instruction.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" #include "revng/ADT/FilteredGraphTraits.h" #include "revng/Support/Debug.h" #include "revng/Support/DebugHelper.h" -#include "revng/Support/IRHelpers.h" - -#include "DLATypeSystem.h" #include "DLAHelpers.h" +#include "DLATypeSystem.h" using namespace llvm; @@ -124,61 +120,19 @@ void LayoutTypeSystem::dumpDotOnFile(const char *FName) const { revng_unreachable(); } - const auto LayoutToTypePtrsIt = LayoutToTypePtrsMap.find(L); - if (LayoutToTypePtrsIt != LayoutToTypePtrsMap.end()) { - DotFile << DoRet; - const auto &TypePtrSet = LayoutToTypePtrsIt->second; - revng_assert(not TypePtrSet.empty()); - StringRef Ret = (TypePtrSet.size() > 1) ? - StringRef(DoRet, sizeof(DoRet) - 1) : - StringRef(NoRet, sizeof(NoRet) - 1); - - for (const dla::LayoutTypePtr &P : TypePtrSet) { - P.print(DotFile); - DotFile << Ret; - - // Collect uses for which P is a pointer operand, so that we can print - // them later for debug - const llvm::Value &PtrV = P.getValue(); - for (const Use &U : PtrV.uses()) { - - const llvm::Value *PtrOp = nullptr; - const User *Usr = U.getUser(); - if (auto *Load = dyn_cast(Usr)) - PtrOp = Load->getPointerOperand(); - else if (auto *Store = dyn_cast(Usr)) - PtrOp = Store->getPointerOperand(); - else - continue; - - if (&PtrV == PtrOp) - PtrUses.push_back(&U); - } - } - } - + DebugPrinter->printNodeContent(*this, L, DotFile); DotFile << "\"];\n"; for (uint64_t AccessSize : L->AccessSizes) { DotFile << " access_size_" << AccessSizeID << " [label=\"Access Size: " << AccessSize; - bool Found = false; - for (const llvm::Use *U : PtrUses) { - if (AccessSize == getLoadStoreSizeFromPtrOpUse(*this, U)) { - auto *I = cast(U->getUser()); - DotFile << "\\\\n" - << "In : " << I->getFunction()->getName() << " : "; - DotFile.write_escaped(dumpToString(I)); - Found = true; - } - } + DebugPrinter->printAccessDetails(*this, L, AccessSize, DotFile); DotFile << "\"];\n"; DotFile << " node_" << L->ID << " -> access_size_" << AccessSizeID << ";\n"; - revng_assert(Found); ++AccessSizeID; } } @@ -245,298 +199,12 @@ LayoutTypeSystemNode *LayoutTypeSystem::createArtificialLayoutType() { LTSN *New = new (NodeAllocator) LayoutTypeSystemNode(NID); revng_assert(New); ++NID; + EqClasses.growBy1(); bool Success = Layouts.insert(New).second; revng_assert(Success); return New; } -static void assertGetLayoutTypePreConditions(const Value *V, unsigned Id) { - // We accept only integers, pointer, and function types (which are actually - // used for representing return types of functions) - const Type *VT = V->getType(); - revng_assert(isa(VT) or isa(VT) - or isa(VT)); - // The only case where we accept Id != max are Functions that return structs - revng_assert(Id == std::numeric_limits::max() - or cast(V)->getReturnType()->isStructTy()); -} - -LayoutTypeSystemNode * -LayoutTypeSystem::getLayoutType(const Value *V, unsigned Id) { - - if (V == nullptr) - return nullptr; - - // Check pre-conditions - assertGetLayoutTypePreConditions(V, Id); - - LayoutTypePtr Key(V, Id); - return TypePtrToLayoutMap.at(Key); -} - -std::pair -LayoutTypeSystem::getOrCreateLayoutType(const Value *V, unsigned Id) { - - if (V == nullptr) - return std::make_pair(nullptr, false); - - // Check pre-conditions - assertGetLayoutTypePreConditions(V, Id); - - LayoutTypePtr Key(V, Id); - auto HintIt = TypePtrToLayoutMap.lower_bound(Key); - if (HintIt != TypePtrToLayoutMap.end() - and not TypePtrToLayoutMap.key_comp()(Key, HintIt->first)) { - return std::make_pair(HintIt->second, false); - } - - LayoutTypeSystemNode *Res = createArtificialLayoutType(); - - // Add the mapping between the new LayoutTypeSystemNode and the LayoutTypePtr - // that is associated to V. - const auto &[_, Ok] = LayoutToTypePtrsMap[Res].insert(Key); - TypePtrToLayoutMap.emplace_hint(HintIt, Key, Res); - revng_assert(Ok); - return std::make_pair(Res, true); -} - -static void assertGetLayoutTypePreConditions(const Value &V) { - const Type *VTy = V.getType(); - // We accept only integers, pointer, structs and and function types (which - // are actually used for representing return types of functions) - revng_assert(isa(VTy) or isa(VTy) - or isa(VTy) or isa(VTy)); -} - -SmallVector -LayoutTypeSystem::getLayoutTypes(const Value &V) { - assertGetLayoutTypePreConditions(V); - SmallVector Results; - const Type *VTy = V.getType(); - if (const auto *F = dyn_cast(&V)) { - auto *RetTy = F->getReturnType(); - if (auto *StructTy = dyn_cast(RetTy)) { - unsigned FieldId = 0; - unsigned FieldNum = StructTy->getNumElements(); - for (; FieldId < FieldNum; ++FieldId) { - auto FieldTy = StructTy->getElementType(FieldId); - revng_assert(isa(FieldTy) or isa(FieldTy)); - Results.push_back(getLayoutType(&V, FieldId)); - } - } else { - revng_assert(isa(VTy) or isa(VTy)); - Results.push_back(getLayoutType(&V)); - } - } else if (auto *StructTy = dyn_cast(VTy)) { - revng_assert(not isa(V)); - - if (isa(&V) or isa(&V)) { - - // Special handling for StructInitializers - const Function *Callee = getCallee(cast(&V)); - if (Callee) { - auto CTags = FunctionTags::TagsSet::from(Callee); - if (CTags.contains(FunctionTags::StructInitializer)) { - - revng_assert(not Callee->isVarArg()); - - auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy->getNumElements() == Callee->arg_size()); - - bool OnlyReturnUses = true; - bool HasReturnUse = false; - auto *Call = cast(&V); - for (const User *U : Call->users()) { - if (isa(U)) { - HasReturnUse = true; - - const Function *Caller = Call->getFunction(); - - if (Results.empty()) - Results = getLayoutTypes(*Caller); - else - revng_assert(Results == getLayoutTypes(*Caller)); - - revng_assert(Results.size() == Callee->arg_size()); - } else { - OnlyReturnUses = false; - } - } - revng_assert(not HasReturnUse or OnlyReturnUses); - } - } - - // If Results are full, we have detected a call to a struct_initializer - // that is returned, so we are done. Otherwise the have to look to for - // extractvalue instructions that are extracting values from the return - // value of the struct_initializer call. - if (Results.empty()) { - - auto *I = cast(&V); - const auto ExtractedValues = getExtractedValuesFromInstruction(I); - - Results.resize(ExtractedValues.size(), {}); - - for (auto &Group : llvm::enumerate(ExtractedValues)) { - const auto &ExtractedSet = Group.value(); - const auto FieldId = Group.index(); - // Inside here we're working on a signle field of the struct. - // ExtractedSet contains all the ExtractValueInst that extract the - // same field of the struct. - // We get or create a layout type for each of them, but they should - // all be the same. - std::optional FieldNode; - for (const llvm::ExtractValueInst *Ext : ExtractedSet) { - LayoutTypeSystemNode *ExtNode = getLayoutType(Ext); - if (FieldNode.has_value()) { - LayoutTypeSystemNode *Node = FieldNode.value(); - revng_assert(not Node or not ExtNode or (Node == ExtNode)); - if (not Node) - Node = ExtNode; - } else { - FieldNode = ExtNode; - } - } - Results[FieldId] = FieldNode.value_or(nullptr); - } - } - - } else { - - SmallVector LeafVals; - if (auto *Ins = dyn_cast(&V)) - LeafVals = getInsertValueLeafOperands(Ins); - else - LeafVals.resize(StructTy->getNumElements(), nullptr); - - for (const Value *LeafVal : LeafVals) - Results.push_back(getLayoutType(LeafVal)); - } - } else { - // For non-struct and non-function types we only add a LayoutTypeSystemNode - Results.push_back(getLayoutType(&V)); - } - return Results; -} - -SmallVector, 2> -LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { - assertGetLayoutTypePreConditions(V); - using GetOrCreateResult = std::pair; - SmallVector Results; - const Type *VTy = V.getType(); - if (const auto *F = dyn_cast(&V)) { - auto *RetTy = F->getReturnType(); - if (auto *StructTy = dyn_cast(RetTy)) { - unsigned FieldId = 0; - unsigned FieldNum = StructTy->getNumElements(); - for (; FieldId < FieldNum; ++FieldId) { - auto FieldTy = StructTy->getElementType(FieldId); - revng_assert(isa(FieldTy) or isa(FieldTy)); - Results.push_back(getOrCreateLayoutType(&V, FieldId)); - } - } else { - revng_assert(isa(VTy) or isa(VTy)); - Results.push_back(getOrCreateLayoutType(&V)); - } - } else if (auto *StructTy = dyn_cast(VTy)) { - revng_assert(not isa(V)); - - if (isa(&V) or isa(&V)) { - - // Special handling for StructInitializers - const Function *Callee = getCallee(cast(&V)); - if (Callee) { - auto CTags = FunctionTags::TagsSet::from(Callee); - if (CTags.contains(FunctionTags::StructInitializer)) { - - revng_assert(not Callee->isVarArg()); - - auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy->getNumElements() == Callee->arg_size()); - - bool OnlyReturnUses = true; - bool HasReturnUse = false; - auto *Call = cast(&V); - for (const User *U : Call->users()) { - if (isa(U)) { - HasReturnUse = true; - - const Function *Caller = Call->getFunction(); - - if (Results.empty()) - Results = getOrCreateLayoutTypes(*Caller); - else - revng_assert(Results == getOrCreateLayoutTypes(*Caller)); - - revng_assert(Results.size() == Callee->arg_size()); - } else { - OnlyReturnUses = false; - } - } - revng_assert(not HasReturnUse or OnlyReturnUses); - } - } - - // If Results are full, we have detected a call to a struct_initializer - // that is returned, so we are done. Otherwise the have to look to for - // extractvalue instructions that are extracting values from the return - // value of the struct_initializer call. - if (Results.empty()) { - - auto *I = cast(&V); - const auto ExtractedValues = getExtractedValuesFromInstruction(I); - - Results.resize(ExtractedValues.size(), {}); - - for (auto &Group : llvm::enumerate(ExtractedValues)) { - const auto &ExtractedSet = Group.value(); - const auto FieldId = Group.index(); - // Inside here we're working on a signle field of the struct. - // ExtractedSet contains all the ExtractValueInst that extract the - // same field of the struct. - // We get or create a layout type for each of them, but they should - // all be the same. - std::optional FieldResult; - for (const llvm::ExtractValueInst *Ext : ExtractedSet) { - GetOrCreateResult ExtResult = getOrCreateLayoutType(Ext); - if (FieldResult.has_value()) { - auto &[Node, New] = FieldResult.value(); - const auto &[ExtNode, ExtNew] = ExtResult; - revng_assert(not ExtNew or ExtNode); - if (not Node) { - Node = ExtNode; - } else if (ExtNode and ExtNode != Node) { - bool AddedLink = addEqualityLink(Node, ExtNode).second; - New |= AddedLink; - } - New |= ExtNew; - } else { - FieldResult = ExtResult; - } - } - Results[FieldId] = FieldResult.value_or(GetOrCreateResult{}); - } - } - - } else { - - SmallVector LeafVals; - if (auto *Ins = dyn_cast(&V)) - LeafVals = getInsertValueLeafOperands(Ins); - else - LeafVals.resize(StructTy->getNumElements(), nullptr); - - for (const Value *LeafVal : LeafVals) - Results.push_back(getOrCreateLayoutType(LeafVal)); - } - } else { - // For non-struct and non-function types we only add a LayoutTypeSystemNode - Results.push_back(getOrCreateLayoutType(&V)); - } - return Results; -} - static void fixPredSucc(LayoutTypeSystemNode *From, LayoutTypeSystemNode *Into) { @@ -610,34 +278,20 @@ using LayoutTypeSystemNodePtrVec = std::vector; void LayoutTypeSystem::mergeNodes(const LayoutTypeSystemNodePtrVec &ToMerge) { revng_assert(ToMerge.size() > 1ULL); LayoutTypeSystemNode *Into = ToMerge[0]; - auto &IntoTypePtrs = LayoutToTypePtrsMap.at(Into); + const unsigned IntoID = Into->ID; + for (LayoutTypeSystemNode *From : llvm::drop_begin(ToMerge, 1)) { revng_assert(From != Into); - revng_log(MergeLog, "Merging: " << From << " Into: " << Into); - - auto ToMergeLayoutToTypePtrsIt = LayoutToTypePtrsMap.find(From); - revng_assert(ToMergeLayoutToTypePtrsIt != LayoutToTypePtrsMap.end()); + revng_log(MergeLog, "Merging: " << From->ID << " Into: " << Into->ID); Into->AccessSizes.insert(From->AccessSizes.begin(), From->AccessSizes.end()); - // Update LayoutToTypePtrsMap, the map that maps each LayoutTypeSystemNode * - // to the set of LayoutTypePtrs that are associated to it. - auto &MergedTypePtrs = ToMergeLayoutToTypePtrsIt->second; - IntoTypePtrs.insert(MergedTypePtrs.begin(), MergedTypePtrs.end()); - - // Update TypePtrToLayoutMap, the inverse map of LayoutToTypePtrsMap - for (auto P : MergedTypePtrs) { - revng_assert(TypePtrToLayoutMap.at(P) == From); - TypePtrToLayoutMap.at(P) = Into; - } + EqClasses.join(IntoID, From->ID); fixPredSucc(From, Into); Into->InterferingInfo = Unknown; - // Clear stuff in LayoutTypeToPtrsMap, because now From must be removed. - LayoutToTypePtrsMap.erase(ToMergeLayoutToTypePtrsIt); - // Remove From from Layouts bool Erased = Layouts.erase(From); revng_assert(Erased); @@ -647,12 +301,9 @@ void LayoutTypeSystem::mergeNodes(const LayoutTypeSystemNodePtrVec &ToMerge) { } void LayoutTypeSystem::removeNode(LayoutTypeSystemNode *ToRemove) { - revng_assert(ToRemove); - auto It = LayoutToTypePtrsMap.find(ToRemove); - revng_assert(It != LayoutToTypePtrsMap.end()); - for (auto P : It->second) - TypePtrToLayoutMap.erase(P); - LayoutToTypePtrsMap.erase(It); + // Join the node's eq class with the removed class + EqClasses.remove(ToRemove->ID); + revng_log(MergeLog, "Removing " << ToRemove->ID << "\n"); const auto IsToRemove = [ToRemove](const LayoutTypeSystemNode::Link &L) { return L.first == ToRemove; @@ -1005,4 +656,76 @@ bool LayoutTypeSystem::verifyInheritanceTree() const { return true; } -} // end namespace dla +unsigned VectEqClasses::growBy1() { + ++NElems; + grow(NElems); + return NElems; +} + +void VectEqClasses::remove(const unsigned A) { + if (RemovedID) + join(A, *RemovedID); + else + RemovedID = A; +} + +bool VectEqClasses::isRemoved(const unsigned ID) const { + // No removed nodes + if (not RemovedID) + return false; + + // Uncompressed map + if (getNumClasses() == 0) + return (findLeader(ID) == findLeader(*RemovedID)); + + // Compressed map + unsigned ElementEqClass = lookupEqClass(ID); + unsigned RemovedEqClass = lookupEqClass(*RemovedID); + return (ElementEqClass == RemovedEqClass); +} + +std::optional VectEqClasses::getEqClassID(const unsigned ID) const { + unsigned EqID = lookupEqClass(ID); + bool IsRemoved = (RemovedID) ? lookupEqClass(*RemovedID) == EqID : false; + + if (IsRemoved) + return {}; + else + return EqID; +} + +std::set VectEqClasses::getEqClass(const unsigned ElemID) const { + std::set EqClass; + + for (unsigned OtherID = 0; OtherID < NElems; OtherID++) + if (haveSameEqClass(ElemID, OtherID)) + EqClass.insert(OtherID); + + return EqClass; +} + +bool VectEqClasses::haveSameEqClass(unsigned ID1, unsigned ID2) const { + // Uncompressed map + if (getNumClasses() == 0) + return findLeader(ID1) == findLeader(ID2); + + // Compressed map + return lookupEqClass(ID1) == lookupEqClass(ID2); +} + +void TSDebugPrinter::printNodeContent(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + llvm::raw_fd_ostream &File) const { + auto EqClasses = TS.getEqClasses(); + + File << DoRet; + if (EqClasses.isRemoved(N->ID)) + File << "Removed" << DoRet; + + File << "Equivalence Class: ["; + for (auto ID : EqClasses.getEqClass(N->ID)) + File << ID << ", "; + File << "]" << DoRet; +} + +} // end namespace dla \ No newline at end of file diff --git a/lib/Decompiler/DLATypeSystem.h b/lib/Decompiler/DLATypeSystem.h index f8c6bb9fe..c77cb91a7 100644 --- a/lib/Decompiler/DLATypeSystem.h +++ b/lib/Decompiler/DLATypeSystem.h @@ -5,6 +5,7 @@ // #include +#include #include #include #include @@ -14,9 +15,9 @@ #include #include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/IntEqClasses.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/IR/Value.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/raw_ostream.h" @@ -144,6 +145,64 @@ inline bool hasValidLayout(const LayoutTypeSystemNode *N) { return not N->AccessSizes.empty(); } +///\brief This class handles equivalence classes between indexes of vectors +class VectEqClasses : public llvm::IntEqClasses { +private: + // ID of the first removed ID + std::optional RemovedID = {}; + unsigned NElems = 0; + +private: + ///\brief Used internally, operator[] is removed for this class + unsigned lookupEqClass(unsigned ID) const { + return llvm::IntEqClasses::operator[](ID); + } + +public: + ///\brief Add 1 element with its own equivalence class + unsigned growBy1(); + + ///\brief Remove the whole equivalence class of \a ID + void remove(const unsigned ID); + + ///\brief Check if the element has been removed + bool isRemoved(const unsigned ID) const; + + ///\brief Get the total number of elements added + unsigned getNumElements() const { return NElems; } + +public: + ///\brief You can't access the Eq Classes directly, some might be deleted + unsigned operator[](unsigned) const = delete; + + ///\brief Get the Equivalence class ID of an element (must be compressed) + ///\return empty if the element is out-of-bounds or has been removed + std::optional getEqClassID(const unsigned ID) const; + + ///\brief Get all the elements that are in the same equivalence class of \a ID + ///\note Expensive: performs a linear scan of all the elements + std::set getEqClass(const unsigned ID) const; + + ///\brief Check if \a ID1 and \a ID2 have the same equivalence class + bool haveSameEqClass(unsigned ID1, unsigned ID2) const; +}; + +///\brief This class is used to print debug information about the TypeSystem +/// +/// Override this to obtain implementation-specific debug prints. +struct TSDebugPrinter { + virtual void printNodeContent(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + llvm::raw_fd_ostream &File) const; + + virtual void printAccessDetails(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + const uint64_t AccessSize, + llvm::raw_fd_ostream &File) const {} + + virtual ~TSDebugPrinter() {} +}; + class LayoutTypeSystem { public: using Node = LayoutTypeSystemNode; @@ -155,7 +214,7 @@ public: return P.get(); } - LayoutTypeSystem(llvm::Module &Mod) : M(Mod) {} + LayoutTypeSystem() : DebugPrinter(new TSDebugPrinter) {} ~LayoutTypeSystem() { for (auto *Layout : Layouts) { @@ -165,29 +224,7 @@ public: Layouts.clear(); } - llvm::Module &getModule() const { return M; } - public: - LayoutTypeSystemNode *getLayoutType(const llvm::Value *V, unsigned Id); - - LayoutTypeSystemNode *getLayoutType(const llvm::Value *V) { - return getLayoutType(V, std::numeric_limits::max()); - }; - - std::pair - getOrCreateLayoutType(const llvm::Value *V, unsigned Id); - - std::pair - getOrCreateLayoutType(const llvm::Value *V) { - return getOrCreateLayoutType(V, std::numeric_limits::max()); - } - - llvm::SmallVector - getLayoutTypes(const llvm::Value &V); - - llvm::SmallVector, 2> - getOrCreateLayoutTypes(const llvm::Value &V); - LayoutTypeSystemNode *createArtificialLayoutType(); protected: @@ -248,14 +285,6 @@ public: public: void mergeNodes(const std::vector &ToMerge); - const llvm::SmallSet * - getLayoutTypePtrs(const LayoutTypeSystemNode *N) const { - auto It = LayoutToTypePtrsMap.find(N); - if (It != LayoutToTypePtrsMap.end()) - return &It->second; - return nullptr; - } - void removeNode(LayoutTypeSystemNode *N); void moveEdges(LayoutTypeSystemNode *OldSrc, @@ -264,9 +293,6 @@ public: int64_t OffsetToSum); private: - // A reference to the associated Module - llvm::Module &M; - uint64_t NID = 0ULL; // Holds all the LayoutTypeSystemNode @@ -277,16 +303,6 @@ private: // TypeLinkTag * in the links inside LayoutTypeSystemNode std::set LinkTags = {}; - // Maps llvm::Value to layout types. - // This map is updated along the way when the DLA algorithm merges - // LayoutTypeSystemNodes that are considered to represent the same type. - std::map TypePtrToLayoutMap = {}; - - // Maps layout types to the set of LayoutTypePtr representing the llvm::Value - // that generated them. - std::map> - LayoutToTypePtrsMap = {}; - public: // Checks that is valid, and returns true if it is, false otherwise bool verifyConsistency() const; @@ -304,6 +320,22 @@ public: bool verifyLeafs() const; // Checks that there are no equality edges. bool verifyNoEquality() const; + +private: + // Equivalence classes between nodes. Each node is identified by an ID. + VectEqClasses EqClasses; + // Object that defines how the content of each node should be printed + std::unique_ptr DebugPrinter; + +public: + unsigned getNID() const { return NID; } + + VectEqClasses &getEqClasses() { return EqClasses; } + const VectEqClasses &getEqClasses() const { return EqClasses; } + + void setDebugPrinter(std::unique_ptr &&Printer) { + DebugPrinter = std::move(Printer); + } }; // end class LayoutTypeSystem } // end namespace dla diff --git a/lib/Decompiler/DLATypeSystemBuilder.cpp b/lib/Decompiler/DLATypeSystemBuilder.cpp new file mode 100644 index 000000000..d4be654cf --- /dev/null +++ b/lib/Decompiler/DLATypeSystemBuilder.cpp @@ -0,0 +1,428 @@ +// +// Copyright (c) rev.ng Srls. See LICENSE.md for details. +// + +#include "llvm/IR/Argument.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Value.h" + +#include "revng/Support/IRHelpers.h" + +#include "DLAHelpers.h" +#include "DLATypeSystemBuilder.h" + +using namespace llvm; +using namespace dla; + +// We use \l here instead of \n, because graphviz has this sick way of saying +// that the text in the node labels should be left-justified +static constexpr const char DoRet[] = "\\l"; + +void LLVMTSDebugPrinter::printNodeContent(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + raw_fd_ostream &File) const { + auto EqClasses = TS.getEqClasses(); + revng_assert(not EqClasses.isRemoved(N->ID)); + + File << DoRet; + for (auto ID : EqClasses.getEqClass(N->ID)) { + if (ID < Values.size()) { + this->Values[ID].print(File); + File << DoRet; + } + } +} + +void LLVMTSDebugPrinter::printAccessDetails(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + const uint64_t AccessSize, + raw_fd_ostream &File) const { + auto EqClasses = TS.getEqClasses(); + revng_assert(not EqClasses.isRemoved(N->ID)); + + File << DoRet; + bool Found = false; + + for (auto ID : EqClasses.getEqClass(N->ID)) { + // Ignore nodes that don't have an associated Value + if (ID >= Values.size()) + continue; + + const llvm::Value &PtrV = this->Values[ID].getValue(); + + // Collect uses for which PtrV is a pointer operand + for (const Use &U : PtrV.uses()) { + const llvm::Value *PtrOp = nullptr; + const User *Usr = U.getUser(); + + if (auto *Load = dyn_cast(Usr)) + PtrOp = Load->getPointerOperand(); + else if (auto *Store = dyn_cast(Usr)) + PtrOp = Store->getPointerOperand(); + else + continue; + + if (&PtrV != PtrOp) + continue; + + unsigned InstrAccessSize = ::getLoadStoreSizeFromPtrOpUse(this->M, &U); + + if (AccessSize == InstrAccessSize) { + auto *I = cast(U.getUser()); + File << "\\\\n" + << "In : " << I->getFunction()->getName() << " : "; + File.write_escaped(dumpToString(I)); + Found = true; + } + } + } + revng_assert(Found or N->ID >= Values.size()); +} + +void DLATypeSystemLLVMBuilder::assertGetLayoutTypePreConditions(const Value *V, + unsigned Id) { + // We accept only integers, pointer, and function types (which are actually + // used for representing return types of functions) + const Type *VT = V->getType(); + revng_assert(isa(VT) or isa(VT) + or isa(VT)); + // The only case where we accept Id != max are Functions that return + // structs + revng_assert(Id == std::numeric_limits::max() + or cast(V)->getReturnType()->isStructTy()); +} + +LayoutTypeSystemNode * +DLATypeSystemLLVMBuilder::getLayoutType(const Value *V, unsigned Id) { + + if (V == nullptr) + return nullptr; + + // Check pre-conditions + assertGetLayoutTypePreConditions(V, Id); + + LayoutTypePtr Key(V, Id); + return VisitedMap.at(Key); +} + +std::pair +DLATypeSystemLLVMBuilder::getOrCreateLayoutType(const Value *V, unsigned Id) { + + if (V == nullptr) + return std::make_pair(nullptr, false); + + // Check pre-conditions + assertGetLayoutTypePreConditions(V, Id); + + LayoutTypePtr Key(V, Id); + auto HintIt = VisitedMap.lower_bound(Key); + if (HintIt != VisitedMap.end() + and not VisitedMap.key_comp()(Key, HintIt->first)) { + return std::make_pair(HintIt->second, false); + } + + LayoutTypeSystemNode *Res = TS.createArtificialLayoutType(); + + VisitedMap.emplace_hint(HintIt, Key, Res); + return std::make_pair(Res, true); +} + +using Builder = DLATypeSystemLLVMBuilder; +void Builder::assertGetLayoutTypePreConditions(const Value &V) { + const Type *VTy = V.getType(); + // We accept only integers, pointer, structs and and function types (which + // are actually used for representing return types of functions) + revng_assert(isa(VTy) or isa(VTy) + or isa(VTy) or isa(VTy)); +} + +SmallVector +DLATypeSystemLLVMBuilder::getLayoutTypes(const Value &V) { + assertGetLayoutTypePreConditions(V); + SmallVector Results; + const Type *VTy = V.getType(); + if (const auto *F = dyn_cast(&V)) { + auto *RetTy = F->getReturnType(); + if (auto *StructTy = dyn_cast(RetTy)) { + unsigned FieldId = 0; + unsigned FieldNum = StructTy->getNumElements(); + for (; FieldId < FieldNum; ++FieldId) { + auto FieldTy = StructTy->getElementType(FieldId); + revng_assert(isa(FieldTy) or isa(FieldTy)); + Results.push_back(getLayoutType(&V, FieldId)); + } + } else { + revng_assert(isa(VTy) or isa(VTy)); + Results.push_back(getLayoutType(&V)); + } + } else if (auto *StructTy = dyn_cast(VTy)) { + revng_assert(not isa(V)); + + if (isa(&V) or isa(&V)) { + + // Special handling for StructInitializers + const Function *Callee = getCallee(cast(&V)); + if (Callee) { + auto CTags = FunctionTags::TagsSet::from(Callee); + if (CTags.contains(FunctionTags::StructInitializer)) { + + revng_assert(not Callee->isVarArg()); + + auto *RetTy = cast(Callee->getReturnType()); + revng_assert(RetTy->getNumElements() == Callee->arg_size()); + + bool OnlyReturnUses = true; + bool HasReturnUse = false; + auto *Call = cast(&V); + for (const User *U : Call->users()) { + if (isa(U)) { + HasReturnUse = true; + + const Function *Caller = Call->getFunction(); + + if (Results.empty()) + Results = getLayoutTypes(*Caller); + else + revng_assert(Results == getLayoutTypes(*Caller)); + + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } + } + revng_assert(not HasReturnUse or OnlyReturnUses); + } + } + + // If Results are full, we have detected a call to a struct_initializer + // that is returned, so we are done. Otherwise the have to look to for + // extractvalue instructions that are extracting values from the return + // value of the struct_initializer call. + if (Results.empty()) { + + auto *I = cast(&V); + const auto ExtractedValues = getExtractedValuesFromInstruction(I); + + Results.resize(ExtractedValues.size(), {}); + + for (auto &Group : enumerate(ExtractedValues)) { + const auto &ExtractedSet = Group.value(); + const auto FieldId = Group.index(); + // Inside here we're working on a signle field of the struct. + // ExtractedSet contains all the ExtractValueInst that extract the + // same field of the struct. + // We get or create a layout type for each of them, but they should + // all be the same. + std::optional FieldNode; + for (const ExtractValueInst *Ext : ExtractedSet) { + LayoutTypeSystemNode *ExtNode = getLayoutType(Ext); + if (FieldNode.has_value()) { + LayoutTypeSystemNode *Node = FieldNode.value(); + revng_assert(not Node or not ExtNode or (Node == ExtNode)); + if (not Node) + Node = ExtNode; + } else { + FieldNode = ExtNode; + } + } + Results[FieldId] = FieldNode.value_or(nullptr); + } + } + + } else { + + SmallVector LeafVals; + if (auto *Ins = dyn_cast(&V)) + LeafVals = getInsertValueLeafOperands(Ins); + else + LeafVals.resize(StructTy->getNumElements(), nullptr); + + for (const Value *LeafVal : LeafVals) + Results.push_back(getLayoutType(LeafVal)); + } + } else { + // For non-struct and non-function types we only add a + // LayoutTypeSystemNode + Results.push_back(getLayoutType(&V)); + } + return Results; +} + +SmallVector, 2> +DLATypeSystemLLVMBuilder::getOrCreateLayoutTypes(const Value &V) { + assertGetLayoutTypePreConditions(V); + using GetOrCreateResult = std::pair; + SmallVector Results; + const Type *VTy = V.getType(); + if (const auto *F = dyn_cast(&V)) { + auto *RetTy = F->getReturnType(); + if (auto *StructTy = dyn_cast(RetTy)) { + unsigned FieldId = 0; + unsigned FieldNum = StructTy->getNumElements(); + for (; FieldId < FieldNum; ++FieldId) { + auto FieldTy = StructTy->getElementType(FieldId); + revng_assert(isa(FieldTy) or isa(FieldTy)); + Results.push_back(getOrCreateLayoutType(&V, FieldId)); + } + } else { + revng_assert(isa(VTy) or isa(VTy)); + Results.push_back(getOrCreateLayoutType(&V)); + } + } else if (auto *StructTy = dyn_cast(VTy)) { + revng_assert(not isa(V)); + + if (isa(&V) or isa(&V)) { + + // Special handling for StructInitializers + const Function *Callee = getCallee(cast(&V)); + if (Callee) { + auto CTags = FunctionTags::TagsSet::from(Callee); + if (CTags.contains(FunctionTags::StructInitializer)) { + + revng_assert(not Callee->isVarArg()); + + auto *RetTy = cast(Callee->getReturnType()); + revng_assert(RetTy->getNumElements() == Callee->arg_size()); + + bool OnlyReturnUses = true; + bool HasReturnUse = false; + auto *Call = cast(&V); + for (const User *U : Call->users()) { + if (isa(U)) { + HasReturnUse = true; + + const Function *Caller = Call->getFunction(); + + if (Results.empty()) + Results = getOrCreateLayoutTypes(*Caller); + else + revng_assert(Results == getOrCreateLayoutTypes(*Caller)); + + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } + } + revng_assert(not HasReturnUse or OnlyReturnUses); + } + } + + // If Results are full, we have detected a call to a struct_initializer + // that is returned, so we are done. Otherwise the have to look to for + // extractvalue instructions that are extracting values from the return + // value of the struct_initializer call. + if (Results.empty()) { + + auto *I = cast(&V); + const auto ExtractedValues = getExtractedValuesFromInstruction(I); + + Results.resize(ExtractedValues.size(), {}); + + for (auto &Group : enumerate(ExtractedValues)) { + const auto &ExtractedSet = Group.value(); + const auto FieldId = Group.index(); + // Inside here we're working on a signle field of the struct. + // ExtractedSet contains all the ExtractValueInst that extract the + // same field of the struct. + // We get or create a layout type for each of them, but they should + // all be the same. + std::optional FieldResult; + for (const ExtractValueInst *Ext : ExtractedSet) { + GetOrCreateResult ExtResult = getOrCreateLayoutType(Ext); + if (FieldResult.has_value()) { + auto &[Node, New] = FieldResult.value(); + const auto &[ExtNode, ExtNew] = ExtResult; + revng_assert(not ExtNew or ExtNode); + if (not Node) { + Node = ExtNode; + } else if (ExtNode and ExtNode != Node) { + bool AddedLink = TS.addEqualityLink(Node, ExtNode).second; + New |= AddedLink; + } + New |= ExtNew; + } else { + FieldResult = ExtResult; + } + } + Results[FieldId] = FieldResult.value_or(GetOrCreateResult{}); + } + } + + } else { + + SmallVector LeafVals; + if (auto *Ins = dyn_cast(&V)) + LeafVals = getInsertValueLeafOperands(Ins); + else + LeafVals.resize(StructTy->getNumElements(), nullptr); + + for (const Value *LeafVal : LeafVals) + Results.push_back(getOrCreateLayoutType(LeafVal)); + } + } else { + // For non-struct and non-function types we only add a + // LayoutTypeSystemNode + Results.push_back(getOrCreateLayoutType(&V)); + } + return Results; +} + +void DLATypeSystemLLVMBuilder::createValuesList() { + this->Values.resize(VisitedMap.size()); + + for (auto &MapIt : VisitedMap) { + LayoutTypePtr Ptr = MapIt.first; + unsigned NodeID = MapIt.second->ID; + revng_assert(NodeID < this->Values.size()); + + this->Values[NodeID] = Ptr; + } +} + +void DLATypeSystemLLVMBuilder::dumpValuesMapping(const llvm::StringRef Name) { + std::error_code EC; + raw_fd_ostream OutFile(Name, EC); + { + using namespace std::string_literals; + revng_check(not EC, ("Cannot open: "s + Name.str()).c_str()); + } + + OutFile << "ID; Value; EqClass\n"; + + for (auto *N : TS.getLayoutsRange()) { + OutFile << N->ID << ";"; + if (N->ID < Values.size()) { + auto &V = Values[N->ID]; + if (isa(V.getValue())) + V.getValue().printAsOperand(OutFile); + else + V.print(OutFile); + } else { + OutFile << "Out of bounds"; + } + OutFile << ";"; + + if (TS.getEqClasses().getNumClasses() == 0) { + OutFile << TS.getEqClasses().findLeader(N->ID); + } else { + auto Class = TS.getEqClasses().getEqClassID(N->ID); + + if (Class) + OutFile << *Class; + else + OutFile << "Removed"; + } + OutFile << "\n"; + } +} + +void DLATypeSystemLLVMBuilder::buildFromLLVMModule(llvm::Module &M, + llvm::ModulePass *MP) { + + TS.setDebugPrinter(std::make_unique(M, this->Values)); + + createInterproceduralTypes(M); + createIntraproceduralTypes(M, MP); + + createValuesList(); +} diff --git a/lib/Decompiler/DLATypeSystemBuilder.h b/lib/Decompiler/DLATypeSystemBuilder.h new file mode 100644 index 000000000..4a64baa59 --- /dev/null +++ b/lib/Decompiler/DLATypeSystemBuilder.h @@ -0,0 +1,128 @@ +#pragma once + +// +// Copyright (c) rev.ng Srls. See LICENSE.md for details. +// + +#include "llvm/Pass.h" + +#include "revng/Support/Assert.h" + +#include "revng-c/Decompiler/DLALayouts.h" + +#include "DLATypeSystem.h" + +namespace dla { + +///\brief This class is used to print LLVM information when debugging the TS +/// +/// Since nodes in the TypeSystem graph only have IDs, which are grouped into +/// equivalence classes, if we want to track each ID back to the original LLVM +/// Value when printing we need to define a special DebugPrinter, that knows +/// which Value is mapped to each ID. +class LLVMTSDebugPrinter : public TSDebugPrinter { +protected: + const llvm::Module &M; + const LayoutTypePtrVect &Values; + +public: + ///\brief Build the Debug printer + /// + ///\param M The LLVM module from which the TS graph was built + ///\param Values Ordered vector, Values are indexed with the ID of the + /// corresponding TypeSystemNode + LLVMTSDebugPrinter(const llvm::Module &M, const LayoutTypePtrVect &Values) : + M(M), Values(Values) {} + + LLVMTSDebugPrinter() = delete; + +public: + ///\brief Print the `llvm::Value`s collapsed inside \a N + void printNodeContent(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + llvm::raw_fd_ostream &DotFile) const override; + + ///\brief Print the instruction that originated a given \a AccessSize of \a N + /// + /// Information on the `load`/`store`s related to a given set of `Value`s is + /// reconstructed on-the-fly, therefore this function is expensive. + void printAccessDetails(const LayoutTypeSystem &TS, + const LayoutTypeSystemNode *N, + const uint64_t AccessSize, + llvm::raw_fd_ostream &DotFile) const override; +}; + +///\brief This class builds a DLA type system from an LLVM module +class DLATypeSystemLLVMBuilder { +public: + using VisitedMapT = std::map; + +private: + ///\brief Separate class that add `Instance` edges + class InstanceLinkAdder; + + ///\brief The TypeSystem to build + LayoutTypeSystem &TS; + + ///\brief Ordered vector, each element is indexed with the ID of the + /// corresponding Node + LayoutTypePtrVect Values; + + ///\brief Reverse map between `llvm::Value`s and Nodes + VisitedMapT VisitedMap; + +private: + void assertGetLayoutTypePreConditions(const llvm::Value *V, unsigned Id); + void assertGetLayoutTypePreConditions(const llvm::Value &V); + + LayoutTypeSystemNode *getLayoutType(const llvm::Value *V, unsigned Id); + + LayoutTypeSystemNode *getLayoutType(const llvm::Value *V) { + return getLayoutType(V, std::numeric_limits::max()); + }; + + std::pair + getOrCreateLayoutType(const llvm::Value *V, unsigned Id); + + std::pair + getOrCreateLayoutType(const llvm::Value *V) { + return getOrCreateLayoutType(V, std::numeric_limits::max()); + } + + llvm::SmallVector + getLayoutTypes(const llvm::Value &V); + + llvm::SmallVector, 2> + getOrCreateLayoutTypes(const llvm::Value &V); + +private: + bool createInterproceduralTypes(llvm::Module &M); + bool createIntraproceduralTypes(llvm::Module &M, llvm::ModulePass *MP); + + ///\brief Collect LayoutTypePtrs and place them in the right position + void createValuesList(); + +public: + LayoutTypePtrVect &getValues() { return Values; } + + ///\brief Print a `.csv` with the mapping between nodes and `llvm::Value`s + /// + /// The mapping is reconstructed on-the-fly, therefore is expensive. The + /// generated .csv uses _semicolons_ as separators. + void dumpValuesMapping(const llvm::StringRef Name); + +public: + DLATypeSystemLLVMBuilder(LayoutTypeSystem &TS) : TS(TS){}; + + ///\brief Create a DLATypeSystem graph for a given LLVM module + /// + /// LayoutTypePtrs represent elements of the LLVM IR that are thought to be + /// possible pointers. The builder's job is to: + /// 1. Identify such LayoutTypePtrs + /// 2. Create a Node for each of them in the DLATypeSystem graph (TS) + /// 3. Keep an ordered vector of LayoutTypePtrs, where each element's index + /// corresponds to the ID of the corresponding LayoutTypeSystemNode generated + void buildFromLLVMModule(llvm::Module &M, llvm::ModulePass *MP); +}; + +} // namespace dla \ No newline at end of file diff --git a/lib/Decompiler/IRASTTypeTranslation.cpp b/lib/Decompiler/IRASTTypeTranslation.cpp index c97b8af8b..3cf6edf1f 100644 --- a/lib/Decompiler/IRASTTypeTranslation.cpp +++ b/lib/Decompiler/IRASTTypeTranslation.cpp @@ -25,9 +25,8 @@ #include "revng-c/Decompiler/DLALayouts.h" -#include "IRASTTypeTranslation.h" - #include "DLATypeSystem.h" +#include "IRASTTypeTranslation.h" #include "Mangling.h" std::string diff --git a/lib/ValueManipulationAnalysis/ContractedGraph.cpp b/lib/ValueManipulationAnalysis/ContractedGraph.cpp index 4f836b91b..632b86bf2 100644 --- a/lib/ValueManipulationAnalysis/ContractedGraph.cpp +++ b/lib/ValueManipulationAnalysis/ContractedGraph.cpp @@ -16,7 +16,6 @@ #include "revng-c/ValueManipulationAnalysis/TypeColors.h" #include "ContractedGraph.h" - #include "TypeFlowGraph.h" #include "TypeFlowNode.h" diff --git a/lib/ValueManipulationAnalysis/Mincut.cpp b/lib/ValueManipulationAnalysis/Mincut.cpp index 7efa151f3..449ae9847 100644 --- a/lib/ValueManipulationAnalysis/Mincut.cpp +++ b/lib/ValueManipulationAnalysis/Mincut.cpp @@ -21,9 +21,8 @@ #include "revng-c/ValueManipulationAnalysis/TypeColors.h" -#include "Mincut.h" - #include "ContractedGraph.h" +#include "Mincut.h" #include "TypeFlowNode.h" using namespace vma; diff --git a/lib/ValueManipulationAnalysis/TypeFlowGraph.cpp b/lib/ValueManipulationAnalysis/TypeFlowGraph.cpp index 0516cface..46cef864a 100644 --- a/lib/ValueManipulationAnalysis/TypeFlowGraph.cpp +++ b/lib/ValueManipulationAnalysis/TypeFlowGraph.cpp @@ -23,7 +23,6 @@ #include "revng-c/ValueManipulationAnalysis/TypeColors.h" #include "TypeFlowGraph.h" - #include "TypeFlowGraphWriter.h" #include "TypeFlowNode.h"