From f7866ddd85705ece7b5faaf2e9559e4598097b88 Mon Sep 17 00:00:00 2001 From: Alvise de Faveri Date: Wed, 15 Jun 2022 17:45:05 +0200 Subject: [PATCH] IRCanonicalization: Add `MakeLocalVariables` pass --- include/revng-c/Support/FunctionTags.h | 9 ++ lib/Backend/DecompileFunction.cpp | 3 +- lib/IRCanonicalization/CMakeLists.txt | 1 + lib/IRCanonicalization/MakeLocalVariables.cpp | 135 ++++++++++++++++++ lib/InitModelTypes/InitModelTypes.cpp | 6 + lib/Support/FunctionTags.cpp | 25 ++++ share/revng/pipelines/ir-canonicalization.yml | 1 + 7 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 lib/IRCanonicalization/MakeLocalVariables.cpp diff --git a/include/revng-c/Support/FunctionTags.h b/include/revng-c/Support/FunctionTags.h index b7b27e193..eeada7420 100644 --- a/include/revng-c/Support/FunctionTags.h +++ b/include/revng-c/Support/FunctionTags.h @@ -25,6 +25,7 @@ extern Tag ModelCast; extern Tag AssignmentMarker; extern Tag OpaqueExtractValue; extern Tag Parentheses; +extern Tag LocalVariable; extern Tag ReadsMemory; extern Tag WritesMemory; @@ -108,3 +109,11 @@ llvm::FunctionType *getOpaqueEVFunctionType(llvm::ExtractValueInst *Extract); // Initializes a pool of OpaqueExtractValue instructions, so that a new one can // be created on-demand. void initOpaqueEVPool(OpaqueFunctionsPool &Pool, llvm::Module *M); + +/// LocalVariable is used to indicate the allocation of a local variable. It +/// returns a reference to the allocated variable. +llvm::FunctionType *getLocalVarType(llvm::Type *ReturnedType); + +/// Initializes a pool of LocalVariable functions, initializing it its internal +/// Module. +void initLocalVarPool(OpaqueFunctionsPool &Pool); diff --git a/lib/Backend/DecompileFunction.cpp b/lib/Backend/DecompileFunction.cpp index a8d67acc9..40a022deb 100644 --- a/lib/Backend/DecompileFunction.cpp +++ b/lib/Backend/DecompileFunction.cpp @@ -773,7 +773,8 @@ StringToken CCodeGenerator::handleSpecialFunction(const llvm::CallInst *Call) { // Use the name of the assigned variable when referencing this value Expression = VarName; - } else if (FuncName.startswith("revng_stack_frame") + } else if (FunctionTags::LocalVariable.isTagOf(CalledFunc) + or FuncName.startswith("revng_stack_frame") or FuncName.startswith("revng_call_stack_arguments")) { const auto &[VarName, New] = getOrCreateVarName(Call); diff --git a/lib/IRCanonicalization/CMakeLists.txt b/lib/IRCanonicalization/CMakeLists.txt index 5cafb0a0e..7d8b9bb8f 100644 --- a/lib/IRCanonicalization/CMakeLists.txt +++ b/lib/IRCanonicalization/CMakeLists.txt @@ -8,6 +8,7 @@ revng_add_analyses_library( revngcIRCanonicalization revngc ExitSSAPass.cpp + MakeLocalVariables.cpp MakeModelCastPass.cpp MakeModelGEPPass.cpp RemovePointerCasts.cpp diff --git a/lib/IRCanonicalization/MakeLocalVariables.cpp b/lib/IRCanonicalization/MakeLocalVariables.cpp new file mode 100644 index 000000000..89970de73 --- /dev/null +++ b/lib/IRCanonicalization/MakeLocalVariables.cpp @@ -0,0 +1,135 @@ +// +// 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); diff --git a/lib/InitModelTypes/InitModelTypes.cpp b/lib/InitModelTypes/InitModelTypes.cpp index 7fabf85ed..f7507fbb0 100644 --- a/lib/InitModelTypes/InitModelTypes.cpp +++ b/lib/InitModelTypes/InitModelTypes.cpp @@ -287,6 +287,12 @@ static TypeVector getReturnTypes(const llvm::CallInst *Call, ReturnTypes.push_back(It->second); } + } else if (FunctionTags::LocalVariable.isTagOf(CalledFunc)) { + StringRef StringOp = extractFromConstantStringPtr(Call->getArgOperand(0)); + const model::QualifiedType VarType = parseQualifiedType(StringOp, Model); + + ReturnTypes.push_back(VarType); + } else if (FunctionTags::StructInitializer.isTagOf(CalledFunc)) { // Struct initializers are only used to pack together return values of // RawFunctionTypes that return multiple values, therefore they have the diff --git a/lib/Support/FunctionTags.cpp b/lib/Support/FunctionTags.cpp index 066e48a09..721ac1df0 100644 --- a/lib/Support/FunctionTags.cpp +++ b/lib/Support/FunctionTags.cpp @@ -26,6 +26,7 @@ Tag ModelGEP(ModelGEPName); Tag AssignmentMarker(MarkerName); Tag OpaqueExtractValue("OpaqueExtractvalue"); Tag Parentheses("Parentheses"); +Tag LocalVariable("LocalVariable"); Tag WritesMemory("WritesMemory"); Tag ReadsMemory("ReadsMemory"); } // namespace FunctionTags @@ -183,6 +184,30 @@ llvm::Function *getAssignmentMarker(llvm::Module &M, llvm::Type *T) { return MarkerF; } +llvm::FunctionType *getLocalVarType(llvm::Type *ReturnedType) { + using namespace llvm; + + // There only argument is a pointer to a constant string that contains a + // serialization of the allocated variable's type + auto &C = ReturnedType->getContext(); + SmallVector FixedArgs = { getStringPtrType(C) }; + return FunctionType::get(ReturnedType, FixedArgs, false /* IsVarArg */); +} + +void initLocalVarPool(OpaqueFunctionsPool &Pool) { + // Set attributes + Pool.addFnAttribute(llvm::Attribute::NoUnwind); + Pool.addFnAttribute(llvm::Attribute::WillReturn); + Pool.addFnAttribute(llvm::Attribute::ReadNone); + // Set revng tags + Pool.setTags({ &FunctionTags::LocalVariable, + &FunctionTags::IsRef, + &FunctionTags::AllocatesLocalVariable }); + // Initialize the pool from its internal llvm::Module if possible. + // Use the stored type as a key. + Pool.initializeFromReturnType(FunctionTags::LocalVariable); +} + llvm::FunctionType *getOpaqueEVFunctionType(llvm::ExtractValueInst *Extract) { using namespace llvm; // First argument is the struct we are extracting from diff --git a/share/revng/pipelines/ir-canonicalization.yml b/share/revng/pipelines/ir-canonicalization.yml index 771d56f3c..b0272b083 100644 --- a/share/revng/pipelines/ir-canonicalization.yml +++ b/share/revng/pipelines/ir-canonicalization.yml @@ -16,6 +16,7 @@ Branches: - dce - exit-ssa - twoscomplement-normalization + - make-local-variables - add-assignment-markers - make-model-cast - operatorprecedence-resolution