blob: 52dee0d6194c79dfd3595e93bd1e7d6a5c4d22e7 [file] [log] [blame]
Pirama Arumuga Nainard2d5ee32016-04-12 14:04:50 -07001/*
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
Jean-Luc Brouilleta2dd52f2017-02-16 20:57:26 -080017#include "bcc/Config.h"
18#include "Assert.h"
19#include "Log.h"
20#include "RSTransforms.h"
Pirama Arumuga Nainard2d5ee32016-04-12 14:04:50 -070021
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}