/* * Copyright 2016, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "bcc/Assert.h" #include "bcc/Config/Config.h" #include "bcc/Support/Log.h" #include "bcc/Renderscript/RSTransforms.h" #include <cstdlib> #include <llvm/IR/Function.h> #include <llvm/IR/Instructions.h> #include <llvm/IR/Module.h> #include <llvm/Pass.h> #include <llvm/IR/GetElementPtrTypeIterator.h> namespace { // anonymous namespace /* This pass translates GEPs that index into structs or arrays of structs to * GEPs with an int8* operand and a byte offset. This translation is done to * enforce on x86 the ARM alignment rule that 64-bit scalars be 8-byte aligned * for structs with such scalars. */ class RSX86TranslateGEPPass : public llvm::FunctionPass { private: static char ID; llvm::LLVMContext *Context; const llvm::DataLayout DL; // Walk a GEP instruction and return true if any type indexed is a struct. bool GEPIndexesStructType(const llvm::GetElementPtrInst *GEP) { for (llvm::gep_type_iterator GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP); GTI != GTE; ++GTI) { if (llvm::dyn_cast<llvm::StructType>(*GTI)) { return true; } } return false; } // Helper method to add two llvm::Value parameters llvm::Value *incrementOffset(llvm::Value *accum, llvm::Value *incr, llvm::Instruction *InsertBefore) { if (accum == nullptr) return incr; return llvm::BinaryOperator::CreateAdd(accum, incr, "", InsertBefore); } // Compute the byte offset for a GEP from the GEP's base pointer operand. // Based on visitGetElementPtrInst in llvm/lib/Transforms/Scalar/SROA.cpp. // The difference is that this function handles non-constant array indices and // constructs a sequence of instructions to calculate the offset. These // instructions might not be the most efficient way to calculate this offset, // but we rely on subsequent optimizations to do necessary fold/combine. llvm::Value *computeGEPOffset(llvm::GetElementPtrInst *GEP) { llvm::Value *Offset = nullptr; for (llvm::gep_type_iterator GTI = gep_type_begin(GEP), GTE = gep_type_end(GEP); GTI != GTE; ++GTI) { if (llvm::StructType *STy = llvm::dyn_cast<llvm::StructType>(*GTI)) { llvm::ConstantInt *OpC = llvm::dyn_cast<llvm::ConstantInt>(GTI.getOperand()); if (!OpC) { ALOGE("Operand for struct type is not constant!"); bccAssert(false); } // Offset = Offset + EltOffset for index into a struct const llvm::StructLayout *SL = DL.getStructLayout(STy); unsigned EltOffset = SL->getElementOffset(OpC->getZExtValue()); llvm::Value *Incr = llvm::ConstantInt::get( llvm::Type::getInt32Ty(*Context), EltOffset); Offset = incrementOffset(Offset, Incr, GEP); } else { // Offset = Offset + Index * EltSize for index into an array or a vector llvm::Value *EltSize = llvm::ConstantInt::get( llvm::Type::getInt32Ty(*Context), DL.getTypeAllocSize(GTI.getIndexedType())); llvm::Value *Incr = llvm::BinaryOperator::CreateMul( GTI.getOperand() /* Index */, EltSize, "", GEP); Offset = incrementOffset(Offset, Incr, GEP); } } return Offset; } void translateGEP(llvm::GetElementPtrInst *GEP) { // cast GEP pointer operand to int8* llvm::CastInst *Int8Ptr = llvm::CastInst::CreatePointerCast( GEP->getPointerOperand(), llvm::Type::getInt8PtrTy(*Context), "to.int8ptr", GEP); llvm::Value *Indices[1] = {computeGEPOffset(GEP)}; // index into the int8* based on the byte offset llvm::GetElementPtrInst *Int8PtrGEP = llvm::GetElementPtrInst::Create( llvm::Type::getInt8Ty(*Context), Int8Ptr, llvm::makeArrayRef(Indices), "int8ptr.indexed", GEP); Int8PtrGEP->setIsInBounds(GEP->isInBounds()); // cast the indexed int8* back to the type of the original GEP llvm::CastInst *OutCast = llvm::CastInst::CreatePointerCast( Int8PtrGEP, GEP->getType(), "to.orig.geptype", GEP); GEP->replaceAllUsesWith(OutCast); } public: RSX86TranslateGEPPass() : FunctionPass (ID), DL(X86_CUSTOM_DL_STRING) { } virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override { // This pass is run in isolation in a separate pass manager. So setting // AnalysisUsage is unnecessary. Set just for completeness. AU.setPreservesCFG(); } virtual bool runOnFunction(llvm::Function &F) override { bool changed = false; Context = &F.getParent()->getContext(); // To avoid updating/deleting instructions while walking a BasicBlock's instructions, // collect the GEPs that need to be translated and process them // subsequently. std::vector<llvm::GetElementPtrInst *> GEPsToHandle; for (auto &BB: F) { for (auto &I: BB) { if (auto *GEP = llvm::dyn_cast<llvm::GetElementPtrInst>(&I)) { if (GEPIndexesStructType(GEP)) { GEPsToHandle.push_back(GEP); } } } } for (auto *GEP: GEPsToHandle) { // Translate GEPs and erase them translateGEP(GEP); changed = true; GEP->eraseFromParent(); } return changed; } virtual const char *getPassName() const override { return "Translate GEPs on structs, intended for x86 target"; } }; } char RSX86TranslateGEPPass::ID = 0; namespace bcc { llvm::FunctionPass * createRSX86TranslateGEPPass() { return new RSX86TranslateGEPPass(); } }