From e34426c03e4bc7ad63d5eaa953515f0d82d3c8c5 Mon Sep 17 00:00:00 2001 From: Pietro Fezzardi Date: Thu, 25 Mar 2021 10:11:25 +0100 Subject: [PATCH] Decompiler: handle PHINode with StructType --- lib/Decompiler/ASTBuildAnalysis.cpp | 2 +- .../DLACreateInterProceduralTypes.cpp | 10 +- .../DLACreateIntraProceduralTypes.cpp | 37 +++--- lib/Decompiler/DLAHelpers.cpp | 107 +++++++++++++----- lib/Decompiler/DLAHelpers.h | 6 +- lib/Decompiler/DLATypeSystem.cpp | 103 +++++++++-------- lib/Decompiler/DecompilationHelpers.cpp | 10 +- lib/Decompiler/DecompilationHelpers.h | 3 +- lib/Decompiler/FuncDeclCreationAction.cpp | 2 +- lib/Decompiler/IRASTTypeTranslation.h | 3 +- 10 files changed, 176 insertions(+), 107 deletions(-) diff --git a/lib/Decompiler/ASTBuildAnalysis.cpp b/lib/Decompiler/ASTBuildAnalysis.cpp index 9a4b36f2d..3865d6f6c 100644 --- a/lib/Decompiler/ASTBuildAnalysis.cpp +++ b/lib/Decompiler/ASTBuildAnalysis.cpp @@ -582,7 +582,7 @@ Stmt *StmtBuilder::buildStmt(Instruction &I) { Value *AggregateOp = Extract->getAggregateOperand(); if (isa(AggregateOp)) return nullptr; - revng_assert(isa(AggregateOp)); + revng_assert(isa(AggregateOp) or isa(AggregateOp)); llvm::Type *AggregateTy = AggregateOp->getType(); revng_assert(AggregateTy->isAggregateType()); auto *TypeDecl = Declarator.lookupTypeDeclOrNull(AggregateTy); diff --git a/lib/Decompiler/DLACreateInterProceduralTypes.cpp b/lib/Decompiler/DLACreateInterProceduralTypes.cpp index f185fe51d..afeea82d2 100644 --- a/lib/Decompiler/DLACreateInterProceduralTypes.cpp +++ b/lib/Decompiler/DLACreateInterProceduralTypes.cpp @@ -70,13 +70,17 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { } } else if (auto *PHI = dyn_cast(&I)) { revng_assert(isa(PHI->getType()) - or isa(PHI->getType())); + or isa(PHI->getType()) + or isa(PHI->getType())); auto PHITypes = TS.getOrCreateLayoutTypes(*PHI); for (const Use &Incoming : PHI->incoming_values()) { revng_assert(isa(Incoming->getType()) - or isa(Incoming->getType())); + or isa(Incoming->getType()) + or isa(Incoming->getType())); auto InTypes = TS.getOrCreateLayoutTypes(*Incoming.get()); - revng_assert(1ULL == PHITypes.size() == InTypes.size()); + revng_assert(PHITypes.size() == InTypes.size()); + revng_assert((PHITypes.size() == 1ULL) + or isa(PHI->getType())); auto FieldNum = PHITypes.size(); for (auto FieldId = 0ULL; FieldId < FieldNum; ++FieldId) { // Incoming type inherits from PHI type diff --git a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp index 60ee9ad8d..be5e20dc3 100644 --- a/lib/Decompiler/DLACreateIntraProceduralTypes.cpp +++ b/lib/Decompiler/DLACreateIntraProceduralTypes.cpp @@ -281,26 +281,33 @@ public: } } } else if (auto *PHI = dyn_cast(&I)) { + // Booleans can not be addresses, so we can skip them. if (PHI->getType()->isIntegerTy(1)) continue; revng_assert(isa(PHI->getType()) - or isa(PHI->getType())); - { + or isa(PHI->getType()) + or isa(PHI->getType())); + if (not isa(PHI->getType())) { + LayoutTypeSystemNode *PHIType = TS.getLayoutType(PHI); const SCEV *PHISCEV = SE->getSCEV(PHI); SCEVToLayoutType.insert(std::make_pair(PHISCEV, PHIType)); + + // PHI Incoming values + for (Value *In : PHI->incoming_values()) { + revng_assert(isa(In->getType()) + or isa(In->getType())); + LayoutTypeSystemNode *InTy = TS.getLayoutType(In); + const SCEV *InSCEV = SE->getSCEV(In); + SCEVToLayoutType.insert(std::make_pair(InSCEV, InTy)); + } + } else { + // If it's a struct is not SCEVable, so there's no point in trying + // to get SCEVs for this. } - // PHI Incoming values - for (Value *In : PHI->incoming_values()) { - revng_assert(isa(In->getType()) - or isa(In->getType())); - LayoutTypeSystemNode *InTy = TS.getLayoutType(In); - const SCEV *InSCEV = SE->getSCEV(In); - SCEVToLayoutType.insert(std::make_pair(InSCEV, InTy)); - } } else if (auto *Sel = dyn_cast(&I)) { // Booleans can not be addresses, so we can skip them. if (Sel->getType()->isIntegerTy(1)) @@ -409,7 +416,7 @@ public: // Types representing the return type auto FormalRetTys = TS.getLayoutTypes(*Callee); auto Size = FormalRetTys.size(); - auto ExtractedVals = getExtractedValuesFromCall(C); + auto ExtractedVals = getExtractedValuesFromInstruction(C); revng_assert(Size == ExtractedVals.size()); for (const auto &[Ext, RetTy] : llvm::zip(ExtractedVals, FormalRetTys)) { @@ -536,7 +543,7 @@ public: bool createBaseAddrWithInstanceLink(LayoutTypeSystem &TS, Value *PointerVal, const BasicBlock &B) { - revng_assert(nullptr != PointerVal); + revng_assert(PointerVal); bool AddedSomething = false; @@ -655,6 +662,9 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { or isa(RetVal)) continue; + if (isa(RetVal)) + continue; + if (auto *Call = dyn_cast(RetVal)) { const Function *Callee = getCallee(Call); @@ -672,6 +682,7 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { auto *InsVal = cast(RetVal); Pointers = getInsertValueLeafOperands(InsVal); + } } else { @@ -717,7 +728,7 @@ bool StepT::runOnTypeSystem(LayoutTypeSystem &TS) { } for (Value *PointerVal : Pointers) { - if (nullptr != PointerVal) + if (PointerVal and not isa(PointerVal->getType())) Changed |= ILA.createBaseAddrWithInstanceLink(TS, PointerVal, *B); } } diff --git a/lib/Decompiler/DLAHelpers.cpp b/lib/Decompiler/DLAHelpers.cpp index 091b0543b..98aee9c61 100644 --- a/lib/Decompiler/DLAHelpers.cpp +++ b/lib/Decompiler/DLAHelpers.cpp @@ -24,22 +24,29 @@ #include "DLATypeSystem.h" +template +concept DerivedValue = std::is_base_of_v; + using std::conditional_t; -template -using LLVMValueT = conditional_t, - const llvm::Value, - llvm::Value>; +template +using PossiblyConstValueT = conditional_t, + std::add_const_t, + std::remove_const_t>; template -concept IsInsertValue = std::is_same_v, - llvm::InsertValueInst>; +concept PossiblyConstInsertValue = std::is_same_v, + llvm::InsertValueInst>; + +template +using ValueT = PossiblyConstValueT; + +template +llvm::SmallVector *, 2> -template -llvm::SmallVector *, 2> getConstQualifiedInsertValueLeafOperands(T *Ins) { - using ValueT = LLVMValueT; - llvm::SmallVector Results; + using ValT = ValueT; + llvm::SmallVector Results; llvm::SmallSet FoundIds; auto *StructTy = llvm::cast(Ins->getType()); unsigned NumFields = StructTy->getNumElements(); @@ -55,12 +62,12 @@ getConstQualifiedInsertValueLeafOperands(T *Ins) { revng_assert(FieldId < NumFields); revng_assert(FoundIds.count(FieldId) == 0); FoundIds.insert(FieldId); - ValueT *Op = Ins->getInsertedValueOperand(); + ValT *Op = Ins->getInsertedValueOperand(); revng_assert(isa(Op->getType()) or isa(Op->getType())); revng_assert(Results[FieldId] == nullptr); Results[FieldId] = Op; - ValueT *Tmp = Ins->getAggregateOperand(); + ValT *Tmp = Ins->getAggregateOperand(); Ins = llvm::dyn_cast(Tmp); if (not Ins) { revng_assert(llvm::isa(Tmp) @@ -81,27 +88,64 @@ getInsertValueLeafOperands(const llvm::InsertValueInst *Ins) { return getConstQualifiedInsertValueLeafOperands(Ins); } -template -using ExtractValueT = conditional_t, - const llvm::ExtractValueInst, - llvm::ExtractValueInst>; -template -concept IsCall = std::is_same_v, llvm::CallInst>; +template +using ExtractValueT = PossiblyConstValueT; -template -using ExtractValuePtrSet = llvm::SmallPtrSet *, 2>; +template +using PHINodeT = PossiblyConstValueT; -template +template +using ExtractValuePtrSet = llvm::SmallPtrSet *, 2>; + +template llvm::SmallVector, 2> -getConstQualifiedExtractedValuesFromCall(T *Call) { +getConstQualifiedExtractedValuesFromInstruction(T *I) { + llvm::SmallVector, 2> Results; - auto *StructTy = llvm::cast(Call->getType()); + + auto *StructTy = llvm::cast(I->getType()); unsigned NumFields = StructTy->getNumElements(); Results.resize(NumFields, {}); - for (auto *Extract : Call->users()) { - auto *E = dyn_cast(Extract); - if (not E) - continue; + + // Find extract value uses transitively, traversing PHIs + ExtractValuePtrSet ExtractValues; + for (auto *TheUser : I->users()) { + if (auto *ExtractV = dyn_cast(TheUser)) { + ExtractValues.insert(ExtractV); + } else if (auto *ThePHI = dyn_cast(TheUser)) { + + // traverse PHIS until we find extractvalues + llvm::SmallPtrSet *, 8> Visited = {}; + llvm::SmallPtrSet *, 8> ToVisit = { ThePHI }; + while (not ToVisit.empty()) { + + llvm::SmallPtrSet *, 8> NextToVisit = {}; + + for (PHINodeT *PHI : ToVisit) { + + Visited.insert(PHI); + NextToVisit.erase(PHI); + + for (auto *User : PHI->users()) { + if (auto *EUser = llvm::dyn_cast(User)) { + ExtractValues.insert(EUser); + } else if (auto *PHIUser = llvm::dyn_cast(User)) { + if (not Visited.count(PHIUser)) + NextToVisit.insert(PHIUser); + } else if (auto *RetUser = llvm::dyn_cast(User)) { + revng_abort("TODO: handle ret user"); + } else { + revng_abort(); + } + } + } + + ToVisit = NextToVisit; + } + } + } + + for (auto *E : ExtractValues) { revng_assert(E->getNumIndices() == 1); unsigned FieldId = E->getIndices()[0]; revng_assert(FieldId < NumFields); @@ -109,17 +153,18 @@ getConstQualifiedExtractedValuesFromCall(T *Call) { or isa(E->getType())); Results[FieldId].insert(E); } + return Results; }; llvm::SmallVector, 2> -getExtractedValuesFromCall(llvm::CallInst *Call) { - return getConstQualifiedExtractedValuesFromCall(Call); +getExtractedValuesFromInstruction(llvm::Instruction *I) { + return getConstQualifiedExtractedValuesFromInstruction(I); } llvm::SmallVector, 2> -getExtractedValuesFromCall(const llvm::CallInst *Call) { - return getConstQualifiedExtractedValuesFromCall(Call); +getExtractedValuesFromInstruction(const llvm::Instruction *I) { + return getConstQualifiedExtractedValuesFromInstruction(I); } uint64_t getLoadStoreSizeFromPtrOpUse(const dla::LayoutTypeSystem &TS, diff --git a/lib/Decompiler/DLAHelpers.h b/lib/Decompiler/DLAHelpers.h index 350e49b75..fb73dd2d5 100644 --- a/lib/Decompiler/DLAHelpers.h +++ b/lib/Decompiler/DLAHelpers.h @@ -10,7 +10,7 @@ namespace llvm { class InsertValueInst; -class CallInst; +class Instruction; class ExtractValueInst; class Use; class Value; @@ -24,11 +24,11 @@ extern llvm::SmallVector getInsertValueLeafOperands(const llvm::InsertValueInst *); extern llvm::SmallVector, 2> -getExtractedValuesFromCall(llvm::CallInst *); +getExtractedValuesFromInstruction(llvm::Instruction *); extern llvm::SmallVector, 2> -getExtractedValuesFromCall(const llvm::CallInst *); +getExtractedValuesFromInstruction(const llvm::Instruction *); namespace dla { diff --git a/lib/Decompiler/DLATypeSystem.cpp b/lib/Decompiler/DLATypeSystem.cpp index f871a903d..454c98a84 100644 --- a/lib/Decompiler/DLATypeSystem.cpp +++ b/lib/Decompiler/DLATypeSystem.cpp @@ -301,37 +301,40 @@ LayoutTypeSystem::getLayoutTypes(const Value &V) { } else if (auto *StructTy = dyn_cast(VTy)) { revng_assert(not isa(V)); - if (auto *Call = dyn_cast(&V)) { + if (isa(&V) or isa(&V)) { // Special handling for StructInitializers - const Function *Callee = getCallee(Call); - auto CTags = FunctionTags::TagsSet::from(Callee); - if (CTags.contains(FunctionTags::StructInitializer)) { + const Function *Callee = getCallee(cast(&V)); + if (Callee) { + auto CTags = FunctionTags::TagsSet::from(Callee); + if (CTags.contains(FunctionTags::StructInitializer)) { - revng_assert(not Callee->isVarArg()); + revng_assert(not Callee->isVarArg()); - auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy->getNumElements() == Callee->arg_size()); + auto *RetTy = cast(Callee->getReturnType()); + revng_assert(RetTy->getNumElements() == Callee->arg_size()); - bool OnlyReturnUses = true; - bool HasReturnUse = false; - for (const User *U : Call->users()) { - if (isa(U)) { - HasReturnUse = true; + 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(); + const Function *Caller = Call->getFunction(); - if (Results.empty()) - Results = getLayoutTypes(*Caller); - else - revng_assert(Results == getLayoutTypes(*Caller)); + if (Results.empty()) + Results = getLayoutTypes(*Caller); + else + revng_assert(Results == getLayoutTypes(*Caller)); - revng_assert(Results.size() == Callee->arg_size()); - } else { - OnlyReturnUses = false; + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } } + revng_assert(not HasReturnUse or OnlyReturnUses); } - revng_assert(not HasReturnUse or OnlyReturnUses); } // If Results are full, we have detected a call to a struct_initializer @@ -340,7 +343,8 @@ LayoutTypeSystem::getLayoutTypes(const Value &V) { // value of the struct_initializer call. if (Results.empty()) { - const auto ExtractedValues = getExtractedValuesFromCall(Call); + auto *I = cast(&V); + const auto ExtractedValues = getExtractedValuesFromInstruction(I); Results.resize(ExtractedValues.size(), {}); @@ -409,37 +413,40 @@ LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { } else if (auto *StructTy = dyn_cast(VTy)) { revng_assert(not isa(V)); - if (auto *Call = dyn_cast(&V)) { + if (isa(&V) or isa(&V)) { // Special handling for StructInitializers - const Function *Callee = getCallee(Call); - auto CTags = FunctionTags::TagsSet::from(Callee); - if (CTags.contains(FunctionTags::StructInitializer)) { + const Function *Callee = getCallee(cast(&V)); + if (Callee) { + auto CTags = FunctionTags::TagsSet::from(Callee); + if (CTags.contains(FunctionTags::StructInitializer)) { - revng_assert(not Callee->isVarArg()); + revng_assert(not Callee->isVarArg()); - auto *RetTy = cast(Callee->getReturnType()); - revng_assert(RetTy->getNumElements() == Callee->arg_size()); + auto *RetTy = cast(Callee->getReturnType()); + revng_assert(RetTy->getNumElements() == Callee->arg_size()); - bool OnlyReturnUses = true; - bool HasReturnUse = false; - for (const User *U : Call->users()) { - if (isa(U)) { - HasReturnUse = true; + 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(); + const Function *Caller = Call->getFunction(); - if (Results.empty()) - Results = getOrCreateLayoutTypes(*Caller); - else - revng_assert(Results == getOrCreateLayoutTypes(*Caller)); + if (Results.empty()) + Results = getOrCreateLayoutTypes(*Caller); + else + revng_assert(Results == getOrCreateLayoutTypes(*Caller)); - revng_assert(Results.size() == Callee->arg_size()); - } else { - OnlyReturnUses = false; + revng_assert(Results.size() == Callee->arg_size()); + } else { + OnlyReturnUses = false; + } } + revng_assert(not HasReturnUse or OnlyReturnUses); } - revng_assert(not HasReturnUse or OnlyReturnUses); } // If Results are full, we have detected a call to a struct_initializer @@ -448,7 +455,8 @@ LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { // value of the struct_initializer call. if (Results.empty()) { - const auto ExtractedValues = getExtractedValuesFromCall(Call); + auto *I = cast(&V); + const auto ExtractedValues = getExtractedValuesFromInstruction(I); Results.resize(ExtractedValues.size(), {}); @@ -467,9 +475,12 @@ LayoutTypeSystem::getOrCreateLayoutTypes(const Value &V) { auto &[Node, New] = FieldResult.value(); const auto &[ExtNode, ExtNew] = ExtResult; revng_assert(not ExtNew or ExtNode); - revng_assert(not Node or not ExtNode or (Node == ExtNode)); - if (not Node) + if (not Node) { Node = ExtNode; + } else if (ExtNode and ExtNode != Node) { + bool AddedLink = addEqualityLink(Node, ExtNode).second; + New |= AddedLink; + } New |= ExtNew; } else { FieldResult = ExtResult; diff --git a/lib/Decompiler/DecompilationHelpers.cpp b/lib/Decompiler/DecompilationHelpers.cpp index 15f03f36b..1f5447df9 100644 --- a/lib/Decompiler/DecompilationHelpers.cpp +++ b/lib/Decompiler/DecompilationHelpers.cpp @@ -67,7 +67,7 @@ std::set getDirectlyUsedGlobals(const Function &F) { return Results; } -std::set getDirectlyCalledFunctions(const Function &F) { +std::set getDirectlyCalledFunctions(Function &F) { std::set Results; for (auto &BB : F) { for (auto &I : BB) { @@ -78,10 +78,10 @@ std::set getDirectlyCalledFunctions(const Function &F) { // UnreachableInst are decompiled as calls to abort, so if F has an // unreachable instruction we need to add "abort" to the called // functions. - auto *Abort = F.getParent()->getFunction("abort"); - revng_assert(nullptr != Abort); - if (Abort) - Results.insert(Abort); + LLVMContext &Ctx = F.getContext(); + Type *Void = Type::getVoidTy(Ctx); + auto Abort = F.getParent()->getOrInsertFunction("abort", Void); + Results.insert(cast(Abort.getCallee())); } } } diff --git a/lib/Decompiler/DecompilationHelpers.h b/lib/Decompiler/DecompilationHelpers.h index 9d3376cbc..6a9f1fea3 100644 --- a/lib/Decompiler/DecompilationHelpers.h +++ b/lib/Decompiler/DecompilationHelpers.h @@ -22,8 +22,7 @@ class Expr; std::set getDirectlyUsedGlobals(const llvm::Function &F); -std::set -getDirectlyCalledFunctions(const llvm::Function &F); +std::set getDirectlyCalledFunctions(llvm::Function &F); clang::CastExpr *createCast(clang::QualType LHSQualTy, clang::Expr *RHS, diff --git a/lib/Decompiler/FuncDeclCreationAction.cpp b/lib/Decompiler/FuncDeclCreationAction.cpp index 0a24bfb3e..1455fa429 100644 --- a/lib/Decompiler/FuncDeclCreationAction.cpp +++ b/lib/Decompiler/FuncDeclCreationAction.cpp @@ -120,7 +120,7 @@ clang::FunctionDecl *DeclCreator::createFunDecl(clang::ASTContext &Context, } void DeclCreator::createFunctionAndCalleesDecl(clang::ASTContext &Ctx, - const llvm::Function *TheF) { + llvm::Function *TheF) { revng_assert(TheF); auto FTags = FunctionTags::TagsSet::from(TheF); diff --git a/lib/Decompiler/IRASTTypeTranslation.h b/lib/Decompiler/IRASTTypeTranslation.h index c60a269a9..7d8fdd7c2 100644 --- a/lib/Decompiler/IRASTTypeTranslation.h +++ b/lib/Decompiler/IRASTTypeTranslation.h @@ -146,8 +146,7 @@ public: void createTypeDeclsForFunctionPrototype(clang::ASTContext &C, const llvm::Function *TheF); - void createFunctionAndCalleesDecl(clang::ASTContext &C, - const llvm::Function *TheF); + void createFunctionAndCalleesDecl(clang::ASTContext &C, llvm::Function *TheF); void createGlobalVarDeclUsedByFunction(clang::ASTContext &Context, const llvm::Function *TheF,