C++程序  |  258行  |  7.95 KB

//===-- CodeMemoryManager.cpp - CodeMemoryManager Class -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See external/llvm/LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
//  This file defines the CodeMemoryManager class.
//
//===----------------------------------------------------------------------===//

#define LOG_TAG "bcc"
#include <bcc/bcc_assert.h>

#include <cutils/log.h>

#include "CodeMemoryManager.h"
#include "ExecutionEngine/OldJIT/ContextManager.h"

#include "llvm/Support/ErrorHandling.h"

#include <sys/mman.h>

#include <stddef.h>

#include <map>
#include <string>
#include <utility>


namespace bcc {


const unsigned int MaxCodeSize = ContextManager::ContextCodeSize;
const unsigned int MaxGOTSize = 1 * 1024;
const unsigned int MaxGlobalVarSize = ContextManager::ContextDataSize;


CodeMemoryManager::CodeMemoryManager()
  : mpCodeMem(NULL), mpGVMem(NULL), mpGOTBase(NULL) {

  reset();
  std::string ErrMsg;

  mpCodeMem = ContextManager::get().allocateContext();

  if (!mpCodeMem) {
    LOGE("Unable to allocate mpCodeMem\n");
    llvm::report_fatal_error("Failed to allocate memory for emitting "
                             "codes\n" + ErrMsg);
  }

  // Set global variable pool
  mpGVMem = mpCodeMem + MaxCodeSize;

  return;
}


CodeMemoryManager::~CodeMemoryManager() {
  mpCodeMem = 0;
  mpGVMem = 0;
}


uint8_t *CodeMemoryManager::allocateSGMemory(uintptr_t Size,
                                             unsigned Alignment) {

  intptr_t FreeMemSize = getFreeCodeMemSize();
  if ((FreeMemSize < 0) || (static_cast<uintptr_t>(FreeMemSize) < Size))
    // The code size excesses our limit
    return NULL;

  if (Alignment == 0)
    Alignment = 1;

  uint8_t *result = getCodeMemBase() + mCurSGMemIdx - Size;
  result = (uint8_t*) (((intptr_t) result) & ~(intptr_t) (Alignment - 1));

  mCurSGMemIdx = result - getCodeMemBase();

  return result;
}


// setMemoryWritable - When code generation is in progress, the code pages
//                     may need permissions changed.
void CodeMemoryManager::setMemoryWritable() {
  mprotect(mpCodeMem, MaxCodeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
}


// When code generation is done and we're ready to start execution, the
// code pages may need permissions changed.
void CodeMemoryManager::setMemoryExecutable() {
  mprotect(mpCodeMem, MaxCodeSize, PROT_READ | PROT_EXEC);
}


// Setting this flag to true makes the memory manager garbage values over
// freed memory.  This is useful for testing and debugging, and is to be
// turned on by default in debug mode.
void CodeMemoryManager::setPoisonMemory(bool poison) {
  // no effect
}


// Global Offset Table Management

// If the current table requires a Global Offset Table, this method is
// invoked to allocate it.  This method is required to set HasGOT to true.
void CodeMemoryManager::AllocateGOT() {
  bccAssert(mpGOTBase != NULL && "Cannot allocate the GOT multiple times");
  mpGOTBase = allocateSGMemory(MaxGOTSize);
  HasGOT = true;
}


// Main Allocation Functions

// When we start JITing a function, the JIT calls this method to allocate a
// block of free RWX memory, which returns a pointer to it. If the JIT wants
// to request a block of memory of at least a certain size, it passes that
// value as ActualSize, and this method returns a block with at least that
// much space. If the JIT doesn't know ahead of time how much space it will
// need to emit the function, it passes 0 for the ActualSize. In either
// case, this method is required to pass back the size of the allocated
// block through ActualSize. The JIT will be careful to not write more than
// the returned ActualSize bytes of memory.
uint8_t *CodeMemoryManager::startFunctionBody(const llvm::Function *F,
                                              uintptr_t &ActualSize) {
  intptr_t FreeMemSize = getFreeCodeMemSize();
  if ((FreeMemSize < 0) ||
      (static_cast<uintptr_t>(FreeMemSize) < ActualSize))
    // The code size excesses our limit
    return NULL;

  ActualSize = getFreeCodeMemSize();
  return (getCodeMemBase() + mCurFuncMemIdx);
}

// This method is called when the JIT is done codegen'ing the specified
// function. At this point we know the size of the JIT compiled function.
// This passes in FunctionStart (which was returned by the startFunctionBody
// method) and FunctionEnd which is a pointer to the actual end of the
// function. This method should mark the space allocated and remember where
// it is in case the client wants to deallocate it.
void CodeMemoryManager::endFunctionBody(const llvm::Function *F,
                                        uint8_t *FunctionStart,
                                        uint8_t *FunctionEnd) {
  bccAssert(FunctionEnd > FunctionStart);
  bccAssert(FunctionStart == (getCodeMemBase() + mCurFuncMemIdx) &&
            "Mismatched function start/end!");

  // Advance the pointer
  intptr_t FunctionCodeSize = FunctionEnd - FunctionStart;
  bccAssert(FunctionCodeSize <= getFreeCodeMemSize() &&
            "Code size excess the limitation!");
  mCurFuncMemIdx += FunctionCodeSize;

  // Record there's a function in our memory start from @FunctionStart
  bccAssert(mFunctionMap.find(F) == mFunctionMap.end() &&
            "Function already emitted!");
  mFunctionMap.insert(
      std::make_pair<const llvm::Function*, std::pair<void*, void*> >(
          F, std::make_pair(FunctionStart, FunctionEnd)));

  return;
}

// Allocate a (function code) memory block of the given size. This method
// cannot be called between calls to startFunctionBody and endFunctionBody.
uint8_t *CodeMemoryManager::allocateSpace(intptr_t Size, unsigned Alignment) {
  if (getFreeCodeMemSize() < Size) {
    // The code size excesses our limit
    return NULL;
  }

  if (Alignment == 0)
    Alignment = 1;

  uint8_t *result = getCodeMemBase() + mCurFuncMemIdx;
  result = (uint8_t*) (((intptr_t) result + Alignment - 1) &
                       ~(intptr_t) (Alignment - 1));

  mCurFuncMemIdx = (result + Size) - getCodeMemBase();

  return result;
}

// Allocate memory for a global variable.
uint8_t *CodeMemoryManager::allocateGlobal(uintptr_t Size, unsigned Alignment) {
  if (getFreeGVMemSize() < Size) {
    // The code size excesses our limit
    LOGE("No Global Memory");
    return NULL;
  }

  if (Alignment == 0)
    Alignment = 1;

  uint8_t *result = getGVMemBase() + mCurGVMemIdx;
  result = (uint8_t*) (((intptr_t) result + Alignment - 1) &
                       ~(intptr_t) (Alignment - 1));

  mCurGVMemIdx = (result + Size) - getGVMemBase();

  return result;
}

// Free the specified function body. The argument must be the return value
// from a call to startFunctionBody() that hasn't been deallocated yet. This
// is never called when the JIT is currently emitting a function.
void CodeMemoryManager::deallocateFunctionBody(void *Body) {
  // linear search
  uint8_t *FunctionStart = NULL, *FunctionEnd = NULL;
  for (FunctionMapTy::iterator I = mFunctionMap.begin(),
          E = mFunctionMap.end(); I != E; I++) {
    if (I->second.first == Body) {
      FunctionStart = reinterpret_cast<uint8_t*>(I->second.first);
      FunctionEnd = reinterpret_cast<uint8_t*>(I->second.second);
      break;
    }
  }

  bccAssert((FunctionStart == NULL) && "Memory is never allocated!");

  // free the memory
  intptr_t SizeNeedMove = (getCodeMemBase() + mCurFuncMemIdx) - FunctionEnd;

  bccAssert(SizeNeedMove >= 0 &&
            "Internal error: CodeMemoryManager::mCurFuncMemIdx may not"
            " be correctly calculated!");

  if (SizeNeedMove > 0) {
    // there's data behind deallocating function
    memmove(FunctionStart, FunctionEnd, SizeNeedMove);
  }

  mCurFuncMemIdx -= (FunctionEnd - FunctionStart);
}

// Below are the methods we create
void CodeMemoryManager::reset() {
  mpGOTBase = NULL;
  HasGOT = false;

  mCurFuncMemIdx = 0;
  mCurSGMemIdx = MaxCodeSize - 1;
  mCurGVMemIdx = 0;

  mFunctionMap.clear();
}

} // namespace bcc