C++程序  |  182行  |  6.21 KB

/*
 * 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/Config.h"
#include "Assert.h"
#include "Log.h"
#include "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();
}

}