diff --git a/include/revng/StackAnalysis/SegregateDirectStackAccesses.h b/include/revng/StackAnalysis/SegregateDirectStackAccesses.h new file mode 100644 index 000000000..202e8cef3 --- /dev/null +++ b/include/revng/StackAnalysis/SegregateDirectStackAccesses.h @@ -0,0 +1,17 @@ +#pragma once + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/IR/PassManager.h" + +#include "revng/Support/revng.h" + +class SegregateDirectStackAccessesPass + : public llvm::PassInfoMixin { + +public: + llvm::PreservedAnalyses + run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM); +}; diff --git a/lib/StackAnalysis/CMakeLists.txt b/lib/StackAnalysis/CMakeLists.txt index 465f1deb1..7d8b16034 100644 --- a/lib/StackAnalysis/CMakeLists.txt +++ b/lib/StackAnalysis/CMakeLists.txt @@ -56,6 +56,7 @@ revng_add_analyses_library_internal(revngStackAnalysis InterproceduralAnalysis.cpp Intraprocedural.cpp PromoteGlobalToLocalVars.cpp + SegregateDirectStackAccesses.cpp StackAnalysis.cpp) llvm_map_components_to_libnames(LLVM_LIBRARIES Analysis) diff --git a/lib/StackAnalysis/SegregateDirectStackAccesses.cpp b/lib/StackAnalysis/SegregateDirectStackAccesses.cpp new file mode 100644 index 000000000..3df82b933 --- /dev/null +++ b/lib/StackAnalysis/SegregateDirectStackAccesses.cpp @@ -0,0 +1,181 @@ +/// \file SegregateDirectStackAccesses.cpp +/// \brief Segregate direct stack accesses from all other memory accesses +/// through alias information. This provides a way to say that stack accesses do +/// not interfere with any other memory access. + +// +// This file is distributed under the MIT License. See LICENSE.md for details. +// + +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/MDBuilder.h" +#include "llvm/IR/PatternMatch.h" + +#include "revng/BasicAnalyses/GeneratedCodeBasicInfo.h" +#include "revng/StackAnalysis/SegregateDirectStackAccesses.h" +#include "revng/Support/Assert.h" + +using namespace llvm; +class SegregateDirectStackAccessesPassImpl; + +using SDSAP = SegregateDirectStackAccessesPass; +using SDSAPI = SegregateDirectStackAccessesPassImpl; + +class SegregateDirectStackAccessesPassImpl { + LLVMContext *Context = nullptr; + GeneratedCodeBasicInfo *GCBI = nullptr; + std::vector DirectStackAccesses; + std::vector NotDirectStackAccesses; + +public: + void run(Function &, FunctionAnalysisManager &); + +private: + void segregateAccesses(Function &F); + void decorateStackAccesses(); +}; + +PreservedAnalyses SDSAP::run(Function &F, FunctionAnalysisManager &FAM) { + SegregateDirectStackAccessesPassImpl SDASP; + SDASP.run(F, FAM); + return PreservedAnalyses::none(); +} + +void SDSAPI::run(Function &F, FunctionAnalysisManager &FAM) { + Context = &(F.getContext()); + + // Get the result of the GCBI analysis + GCBI = &(FAM.getResult(F)); + revng_assert(GCBI != nullptr); + + // Populate the two buckets with all load and store instruction of the + // function, properly segregated. + segregateAccesses(F); + + // Adorn IR with the alias information collected before. + decorateStackAccesses(); +} + +void SDSAPI::segregateAccesses(Function &F) { + using namespace PatternMatch; + + Value *LoadSP = nullptr; + bool Found = false; + + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (auto *LI = dyn_cast(&I)) { + if (GCBI->isSPReg(skipCasts(LI->getPointerOperand()))) { + revng_assert(!Found); + LoadSP = LI; + Found = true; + break; + } + } + } + + if (Found) + break; + } + + // Modify the IR after alloca instructions, if they exist. + auto It = F.getEntryBlock().begin(); + while (It->getOpcode() == Instruction::Alloca) + It++; + IRBuilder<> Builder(&(*It)); + + // Context: inttoptr instructions basically inhibits all optimizations. In + // particular, when an integer is inttoptr'd twice with different destination + // type, alias analysis messes up. Hence, we need to ensure that no inttoptr + // exists when operating on a instruction that directly accesses the stack. + // Note that this problem will be addressed by opaque pointers in the future. + auto *I8PtrTy = Builder.getInt8PtrTy(); + auto *CE = ConstantExpr::getBitCast(GCBI->spReg(), I8PtrTy->getPointerTo()); + Value *SPI8Ptr = Builder.CreateLoad(I8PtrTy, CE); + + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + Value *Pointer = nullptr; + + // Differentiate accesses and add them onto their respective bucket. + // Everything that is not a direct access on the stack is put onto the + // bucket `NotDirectStackAccesses`. Load/store that access the CSVs will + // have their alias info added later as well. + if (auto *LI = dyn_cast(&I)) + Pointer = skipCasts(LI->getPointerOperand()); + else if (auto *SI = dyn_cast(&I)) + Pointer = skipCasts(SI->getPointerOperand()); + + if (Pointer != nullptr) { + Value *LoadPtr = nullptr; + Value *LHS = nullptr; + Value *BitCast = nullptr; + ConstantInt *Offset = nullptr; + Builder.SetInsertPoint(&I); + + // Do we have a inttoptr to a load i64, i64* LoadPtr as pointer operand + // of the current instruction, where LoadPtr is SP? Change it with the + // newly-created bitcasted load in order to prevent from using inttoptr. + if (Pointer == LoadSP) { + Type *DestTy; + if (isa(&I)) + DestTy = I.getOperand(0)->getType(); + else + DestTy = I.getOperand(0)->getType()->getPointerTo(); + + BitCast = Builder.CreateBitCast(SPI8Ptr, DestTy); + I.setOperand(isa(&I) ? 0 : 1, BitCast); + + DirectStackAccesses.emplace_back(&I); + } else if (match(Pointer, m_c_Add(m_Value(LHS), m_ConstantInt(Offset))) + && LHS == LoadSP) { + // Do we have a inttoptr whose pointer operand is an instruction `add + // i64 LHS, X` with X negative constant and LHS the stack pointer? If + // so, canonicalize the i2p + add into a gep whose value is bitcasted + // to the original type of SP. + auto *GEP = Builder.CreateGEP(Builder.getInt8Ty(), SPI8Ptr, Offset); + + Type *DestTy; + if (isa(&I)) + DestTy = I.getOperand(0)->getType(); + else + DestTy = I.getOperand(0)->getType()->getPointerTo(); + + BitCast = Builder.CreateBitCast(GEP, DestTy); + I.setOperand(isa(&I) ? 0 : 1, BitCast); + + DirectStackAccesses.emplace_back(&I); + } else { + NotDirectStackAccesses.emplace_back(&I); + } + } + } + } +} + +void SDSAPI::decorateStackAccesses() { + MDBuilder MDB(*Context); + + MDNode *AliasDomain = MDB.createAliasScopeDomain("CSVAliasDomain"); + + // Create two different domains, one will be populated with memory operations + // that directly access the stack; the other one, with all the remaining kind + // of accesses (to the CSVs, to the heap, etc.). + auto *DSAScope = MDB.createAliasScope("DirectStackAccessScope", AliasDomain); + auto *NDSAScope = MDB.createAliasScope("Not(DirectStackAccessScope)", + AliasDomain); + + auto *DSASet = MDNode::get(*Context, ArrayRef({ DSAScope })); + auto *NDSASet = MDNode::get(*Context, ArrayRef({ NDSAScope })); + + for (auto *I : DirectStackAccesses) { + I->setMetadata(LLVMContext::MD_alias_scope, DSASet); + I->setMetadata(LLVMContext::MD_noalias, NDSASet); + } + + for (auto *I : NotDirectStackAccesses) { + I->setMetadata(LLVMContext::MD_alias_scope, NDSASet); + I->setMetadata(LLVMContext::MD_noalias, DSASet); + } +}