• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "bcc/Assert.h"
18 #include "bcc/Config/Config.h"
19 #include "bcc/Support/Log.h"
20 #include "bcc/Renderscript/RSTransforms.h"
21 
22 #include <cstdlib>
23 
24 #include <llvm/IR/Function.h>
25 #include <llvm/IR/Instructions.h>
26 #include <llvm/IR/Module.h>
27 #include <llvm/Pass.h>
28 #include <llvm/IR/GetElementPtrTypeIterator.h>
29 
30 namespace { // anonymous namespace
31 
32 /* This pass translates GEPs that index into structs or arrays of structs to
33  * GEPs with an int8* operand and a byte offset.  This translation is done to
34  * enforce on x86 the ARM alignment rule that 64-bit scalars be 8-byte aligned
35  * for structs with such scalars.
36  */
37 class RSX86TranslateGEPPass : public llvm::FunctionPass {
38 private:
39   static char ID;
40   llvm::LLVMContext *Context;
41   const llvm::DataLayout DL;
42 
43   // Walk a GEP instruction and return true if any type indexed is a struct.
GEPIndexesStructType(const llvm::GetElementPtrInst * GEP)44   bool GEPIndexesStructType(const llvm::GetElementPtrInst *GEP) {
45     for (llvm::gep_type_iterator GTI = gep_type_begin(GEP),
46                                  GTE = gep_type_end(GEP);
47          GTI != GTE; ++GTI) {
48       if (llvm::dyn_cast<llvm::StructType>(*GTI)) {
49         return true;
50       }
51     }
52     return false;
53   }
54 
55   // Helper method to add two llvm::Value parameters
incrementOffset(llvm::Value * accum,llvm::Value * incr,llvm::Instruction * InsertBefore)56   llvm::Value *incrementOffset(llvm::Value *accum, llvm::Value *incr,
57                                llvm::Instruction *InsertBefore) {
58     if (accum == nullptr)
59       return incr;
60     return llvm::BinaryOperator::CreateAdd(accum, incr, "", InsertBefore);
61   }
62 
63   // Compute the byte offset for a GEP from the GEP's base pointer operand.
64   // Based on visitGetElementPtrInst in llvm/lib/Transforms/Scalar/SROA.cpp.
65   // The difference is that this function handles non-constant array indices and
66   // constructs a sequence of instructions to calculate the offset.  These
67   // instructions might not be the most efficient way to calculate this offset,
68   // but we rely on subsequent optimizations to do necessary fold/combine.
computeGEPOffset(llvm::GetElementPtrInst * GEP)69   llvm::Value *computeGEPOffset(llvm::GetElementPtrInst *GEP) {
70     llvm::Value *Offset = nullptr;
71 
72     for (llvm::gep_type_iterator GTI = gep_type_begin(GEP),
73                                  GTE = gep_type_end(GEP);
74          GTI != GTE; ++GTI) {
75       if (llvm::StructType *STy = llvm::dyn_cast<llvm::StructType>(*GTI)) {
76         llvm::ConstantInt *OpC = llvm::dyn_cast<llvm::ConstantInt>(GTI.getOperand());
77         if (!OpC) {
78           ALOGE("Operand for struct type is not constant!");
79           bccAssert(false);
80         }
81 
82         // Offset = Offset + EltOffset for index into a struct
83         const llvm::StructLayout *SL = DL.getStructLayout(STy);
84         unsigned EltOffset = SL->getElementOffset(OpC->getZExtValue());
85         llvm::Value *Incr = llvm::ConstantInt::get(
86                                 llvm::Type::getInt32Ty(*Context), EltOffset);
87         Offset = incrementOffset(Offset, Incr, GEP);
88       } else {
89         // Offset = Offset + Index * EltSize for index into an array or a vector
90         llvm::Value *EltSize = llvm::ConstantInt::get(
91                                  llvm::Type::getInt32Ty(*Context),
92                                  DL.getTypeAllocSize(GTI.getIndexedType()));
93         llvm::Value *Incr = llvm::BinaryOperator::CreateMul(
94                                 GTI.getOperand() /* Index */,
95                                 EltSize, "", GEP);
96         Offset = incrementOffset(Offset, Incr, GEP);
97       }
98     }
99 
100     return Offset;
101   }
102 
translateGEP(llvm::GetElementPtrInst * GEP)103   void translateGEP(llvm::GetElementPtrInst *GEP) {
104     // cast GEP pointer operand to int8*
105     llvm::CastInst *Int8Ptr = llvm::CastInst::CreatePointerCast(
106                                   GEP->getPointerOperand(),
107                                   llvm::Type::getInt8PtrTy(*Context),
108                                   "to.int8ptr",
109                                   GEP);
110     llvm::Value *Indices[1] = {computeGEPOffset(GEP)};
111 
112     // index into the int8* based on the byte offset
113     llvm::GetElementPtrInst *Int8PtrGEP = llvm::GetElementPtrInst::Create(
114         llvm::Type::getInt8Ty(*Context), Int8Ptr, llvm::makeArrayRef(Indices),
115         "int8ptr.indexed", GEP);
116     Int8PtrGEP->setIsInBounds(GEP->isInBounds());
117 
118     // cast the indexed int8* back to the type of the original GEP
119     llvm::CastInst *OutCast = llvm::CastInst::CreatePointerCast(
120         Int8PtrGEP, GEP->getType(), "to.orig.geptype", GEP);
121 
122     GEP->replaceAllUsesWith(OutCast);
123   }
124 
125 public:
RSX86TranslateGEPPass()126   RSX86TranslateGEPPass()
127     : FunctionPass (ID), DL(X86_CUSTOM_DL_STRING) {
128   }
129 
getAnalysisUsage(llvm::AnalysisUsage & AU) const130   virtual void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
131     // This pass is run in isolation in a separate pass manager.  So setting
132     // AnalysisUsage is unnecessary.  Set just for completeness.
133     AU.setPreservesCFG();
134   }
135 
runOnFunction(llvm::Function & F)136   virtual bool runOnFunction(llvm::Function &F) override {
137     bool changed = false;
138     Context = &F.getParent()->getContext();
139 
140     // To avoid updating/deleting instructions while walking a BasicBlock's instructions,
141     // collect the GEPs that need to be translated and process them
142     // subsequently.
143     std::vector<llvm::GetElementPtrInst *> GEPsToHandle;
144 
145     for (auto &BB: F) {
146       for (auto &I: BB) {
147         if (auto *GEP = llvm::dyn_cast<llvm::GetElementPtrInst>(&I)) {
148           if (GEPIndexesStructType(GEP)) {
149             GEPsToHandle.push_back(GEP);
150           }
151         }
152       }
153     }
154 
155     for (auto *GEP: GEPsToHandle) {
156       // Translate GEPs and erase them
157       translateGEP(GEP);
158       changed = true;
159       GEP->eraseFromParent();
160     }
161 
162     return changed;
163   }
164 
getPassName() const165   virtual const char *getPassName() const override {
166     return "Translate GEPs on structs, intended for x86 target";
167   }
168 };
169 
170 }
171 
172 char RSX86TranslateGEPPass::ID = 0;
173 
174 namespace bcc {
175 
176 llvm::FunctionPass *
createRSX86TranslateGEPPass()177 createRSX86TranslateGEPPass() {
178   return new RSX86TranslateGEPPass();
179 }
180 
181 }
182