// // This file is distributed under the MIT License. See LICENSE.md for details. // #include "llvm/ADT/PostOrderIterator.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Type.h" #include "llvm/Pass.h" #include "revng/ABI/ModelHelpers.h" #include "revng/Model/FunctionTags.h" #include "revng/Model/IRHelpers.h" #include "revng/Model/LoadModelPass.h" #include "revng/Support/Assert.h" #include "revng/Support/DecompilationHelpers.h" #include "revng/Support/IRBuilder.h" #include "revng/Support/YAMLTraits.h" static Logger Log{ "fold-model-gep" }; struct FoldModelGEP : public llvm::FunctionPass { public: static char ID; FoldModelGEP() : FunctionPass(ID) {} /// Fold all ModelGEP(type1, AddressOf(type2, %value)) to %value when type1 /// and type2 are the same bool runOnFunction(llvm::Function &F) override; void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.addRequired(); AU.setPreservesCFG(); } }; using llvm::CallInst; using llvm::dyn_cast; using llvm::StringRef; inline llvm::Value *traverseTransparentInstructions(llvm::Value *V) { llvm::Value *CurValue = V; while (isa(CurValue) or isa(CurValue) or isa(CurValue) or isa(CurValue)) CurValue = cast(CurValue)->getOperand(0); return CurValue; } static llvm::Value *getValueToSubstitute(llvm::Instruction &I, const model::Binary &Model) { if (auto *Call = getCallToTagged(&I, FunctionTags::ModelGEP)) { revng_log(Log, "--------Call: " << dumpToString(I)); // For ModelGEPs, we want to match patterns of the type // `ModelGEP(AddressOf())` where the model types recovered by both the // ModelGEP and the AddressOf are the same. // In this way, we are matching patterns that give birth to expression // such as `*&` and `(&base)->` in the decompiled code, which are // redundant. // First argument is the model type of the base pointer llvm::Value *GEPFirstArg = Call->getArgOperand(0); auto GEPBaseType = fromLLVMString(GEPFirstArg, Model); // Second argument is the base pointer llvm::Value *SecondArg = Call->getArgOperand(1); SecondArg = traverseTransparentInstructions(SecondArg); // Skip if the ModelGEP is doing an array access. If it's doing an array // access, we'd lose that by folding, so we only traverse if it's constant // and it's zero. llvm::Value *ThirdArg = Call->getArgOperand(2); auto *ConstantArrayIndex = dyn_cast(ThirdArg); bool HasInitialArrayAccess = not ConstantArrayIndex or not ConstantArrayIndex->isZero(); if (HasInitialArrayAccess) return nullptr; // Skip if the second argument (after traversing casts) is not an // AddressOf call llvm::CallInst *AddrOfCall = getCallToTagged(SecondArg, FunctionTags::AddressOf); if (not AddrOfCall) return nullptr; revng_log(Log, "Second arg is an addressOf "); // First argument of the AddressOf is the pointer's base type llvm::Value *AddrOfFirstArg = AddrOfCall->getArgOperand(0); auto AddrOfBaseType = fromLLVMString(AddrOfFirstArg, Model); // Skip if the ModelGEP is dereferencing the AddressOf with a // different type if (AddrOfBaseType != GEPBaseType) return nullptr; revng_log(Log, "Types are the same "); revng_log(Log, "Adding " << dumpToString(Call) << " to the map"); return AddrOfCall->getArgOperand(1); } return nullptr; } bool FoldModelGEP::runOnFunction(llvm::Function &F) { // Get the model const auto &Model = getAnalysis().get().getReadOnlyModel().get(); // Initialize the IR builder to inject functions llvm::LLVMContext &LLVMCtx = F.getContext(); // Here we should definitely use the builder that checks the debug info, // but since this going to go away soon, let it stay as is. revng::NonDebugInfoCheckingIRBuilder Builder(LLVMCtx); bool Modified = false; revng_log(Log, "=========Function: " << F.getName()); llvm::SmallVector ToErase; // Collect ModelGEPs for (auto *BB : llvm::ReversePostOrderTraversal(&F)) { for (auto &I : llvm::make_early_inc_range(*BB)) { if (llvm::Value *ValueToSubstitute = getValueToSubstitute(I, *Model)) { auto *CallToFold = cast(&I); revng_assert(isCallToTagged(CallToFold, FunctionTags::ModelGEP)); Builder.SetInsertPoint(CallToFold); if (auto *I = dyn_cast(ValueToSubstitute)) { Builder.SetCurrentDebugLocation(I->getDebugLoc()); } llvm::SmallVector Args; for (auto &Group : llvm::enumerate(CallToFold->args())) { llvm::Value *Arg = Group.value(); // We just ignore the argument representing the array index for the // ModelGEPRef. if (Group.index() == 2) { revng_assert(isa(Arg) and cast(Arg)->isZero()); continue; } Args.push_back(Arg); } Args[1] = ValueToSubstitute; auto *ModelGEPRefFunc = getModelGEPRef(*F.getParent(), CallToFold->getType(), ValueToSubstitute->getType()); llvm::Value *ModelGEPRef = Builder.CreateCall(ModelGEPRefFunc, Args); CallToFold->replaceAllUsesWith(ModelGEPRef); CallToFold->eraseFromParent(); Modified = true; } } } return Modified; } char FoldModelGEP::ID = 0; static llvm::RegisterPass X("fold-model-gep", "Folds ModelGEPs/AddressOf " "patterns that might produce *&, &* " "or (&...)-> expressions " "in the decompiled code.", false, false);