// // Copyright rev.ng Labs Srl. See LICENSE.md for details. // #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Instruction.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/IR/Value.h" #include "llvm/Pass.h" #include "revng/Model/IRHelpers.h" #include "revng/Model/LoadModelPass.h" #include "revng/Support/OpaqueFunctionsPool.h" #include "revng-c/Support/FunctionTags.h" #include "revng-c/Support/ModelHelpers.h" static Logger<> Log{ "make-local-variables" }; struct MakeLocalVariables : public llvm::FunctionPass { public: static char ID; MakeLocalVariables() : FunctionPass(ID) {} /// Transform allocas and alloca-like function calls into calls to /// `LocalVariable(AddressOf())` bool runOnFunction(llvm::Function &F) override; void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { AU.addRequired(); AU.setPreservesCFG(); } }; using llvm::AllocaInst; using llvm::dyn_cast; bool MakeLocalVariables::runOnFunction(llvm::Function &F) { // Skip non-isolated functions auto FTags = FunctionTags::TagsSet::from(&F); if (not FTags.contains(FunctionTags::Isolated)) return false; // Get the model const auto &Model = getAnalysis().get().getReadOnlyModel().get(); llvm::SmallVector ToReplace; // Collect instructions that allocate local variables for (auto &BB : F) for (auto &I : BB) if (auto *Alloca = dyn_cast(&I)) ToReplace.push_back(Alloca); if (ToReplace.empty()) return false; llvm::LLVMContext &LLVMCtx = F.getContext(); llvm::Module &M = *F.getParent(); llvm::IRBuilder<> Builder(LLVMCtx); llvm::Type *PtrSizedInteger = getPointerSizedInteger(LLVMCtx, *Model); // Initialize function pools OpaqueFunctionsPool AddressOfPool(&M, false); initAddressOfPool(AddressOfPool, &M); OpaqueFunctionsPool LocalVarPool(&M, false); initLocalVarPool(LocalVarPool); for (auto *Alloca : ToReplace) { Builder.SetInsertPoint(Alloca); llvm::Type *ResultType = Alloca->getType(); // Convert the allocated llvm type to a model type auto AllocatedType = llvmIntToModelType(Alloca->getAllocatedType(), *Model); llvm::Constant *ModelTypeString = serializeToLLVMString(AllocatedType, M); auto LocalVarLLVMType = llvm::IntegerType::get(LLVMCtx, AllocatedType.size().value() * 8); // Inject call to LocalVariable auto *LocalVarFunctionType = getLocalVarType(LocalVarLLVMType); auto *LocalVarFunction = LocalVarPool.get(LocalVarLLVMType, LocalVarFunctionType, "LocalVariable"); auto *LocalVarCall = Builder.CreateCall(LocalVarFunction, { ModelTypeString }); // Inject a call to AddressOf auto LocalVarType = LocalVarCall->getType(); auto *AddressOfFunctionType = getAddressOfType(PtrSizedInteger, LocalVarType); auto *AddressOfFunction = AddressOfPool.get({ PtrSizedInteger, LocalVarType }, AddressOfFunctionType, "AddressOf"); llvm::Instruction *AddressOfCall = Builder.CreateCall(AddressOfFunction, { ModelTypeString, LocalVarCall }); AddressOfCall->copyMetadata(*Alloca); llvm::Value *ValueToSubstitute = AddressOfCall; // LocalVar and AddressOf work on LLVM integers that represent // pointers in the binary. If we are actually using these as // pointers in LLVM IR, we need to cast them to the appropriate // type. if (ResultType->isPointerTy()) ValueToSubstitute = Builder.CreateIntToPtr(AddressOfCall, ResultType); revng_assert(ResultType == ValueToSubstitute->getType()); Alloca->replaceAllUsesWith(ValueToSubstitute); Alloca->eraseFromParent(); } return true; } char MakeLocalVariables::ID = 0; static llvm::RegisterPass X("make-local-variables", "Replace all opcodes " "that " "declare local " "variables with " "a call to " "LocalVariable.", false, false);