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
30namespace { // 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 */
37class RSX86TranslateGEPPass : public llvm::FunctionPass {
38private:
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.
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
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.
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
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
125public:
126  RSX86TranslateGEPPass()
127    : FunctionPass (ID), DL(X86_CUSTOM_DL_STRING) {
128  }
129
130  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
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
165  virtual const char *getPassName() const override {
166    return "Translate GEPs on structs, intended for x86 target";
167  }
168};
169
170}
171
172char RSX86TranslateGEPPass::ID = 0;
173
174namespace bcc {
175
176llvm::FunctionPass *
177createRSX86TranslateGEPPass() {
178  return new RSX86TranslateGEPPass();
179}
180
181}
182