C++程序  |  247行  |  9.29 KB

//===-- CodeMemoryManager.h - CodeMemoryManager Class -----------*- C++ -*-===//
//
//                     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.
//
//===----------------------------------------------------------------------===//

#ifndef BCC_CODEMEMORYMANAGER_H
#define BCC_CODEMEMORYMANAGER_H

#include "ExecutionEngine/Compiler.h"

#include "llvm/ExecutionEngine/JITMemoryManager.h"

#include <bcc/bcc_assert.h>

#include <map>
#include <utility>

#include <stddef.h>
#include <stdint.h>


namespace llvm {
  // Forward Declaration
  class Function;
  class GlobalValue;
};


namespace bcc {

  //////////////////////////////////////////////////////////////////////////////
  // Memory manager for the code reside in memory
  //
  // The memory for our code emitter is very simple and is conforming to the
  // design decisions of Android RenderScript's Exection Environment:
  //   The code, data, and symbol sizes are limited (currently 100KB.)
  //
  // It's very different from typical compiler, which has no limitation
  // on the code size. How does code emitter know the size of the code
  // it is about to emit? It does not know beforehand. We want to solve
  // this without complicating the code emitter too much.
  //
  // We solve this by pre-allocating a certain amount of memory,
  // and then start the code emission. Once the buffer overflows, the emitter
  // simply discards all the subsequent emission but still has a counter
  // on how many bytes have been emitted.
  //
  // So once the whole emission is done, if there's a buffer overflow,
  // it re-allocates the buffer with enough size (based on the
  //  counter from previous emission) and re-emit again.

  extern const unsigned int MaxCodeSize;
  extern const unsigned int MaxGOTSize;
  extern const unsigned int MaxGlobalVarSize;


  class CodeMemoryManager : public llvm::JITMemoryManager {
  private:
    typedef std::map<const llvm::Function*,
                     std::pair<void * /* start address */,
                               void * /* end address */> > FunctionMapTy;


  private:
    //
    // Our memory layout is as follows:
    //
    //  The direction of arrows (-> and <-) shows memory's growth direction
    //  when more space is needed.
    //
    // @mpCodeMem:
    //  +--------------------------------------------------------------+
    //  | Function Memory ... ->                <- ...        Stub/GOT |
    //  +--------------------------------------------------------------+
    //  |<------------------ Total: @MaxCodeSize KiB ----------------->|
    //
    //  Where size of GOT is @MaxGOTSize KiB.
    //
    // @mpGVMem:
    //  +--------------------------------------------------------------+
    //  | Global variable ... ->                                       |
    //  +--------------------------------------------------------------+
    //  |<--------------- Total: @MaxGlobalVarSize KiB --------------->|
    //
    //
    // @mCurFuncMemIdx: The current index (starting from 0) of the last byte
    //                    of function code's memory usage
    // @mCurSGMemIdx: The current index (starting from tail) of the last byte
    //                    of stub/GOT's memory usage
    // @mCurGVMemIdx: The current index (starting from tail) of the last byte
    //                    of global variable's memory usage
    //
    uintptr_t mCurFuncMemIdx;
    uintptr_t mCurSGMemIdx;
    uintptr_t mCurGVMemIdx;
    char *mpCodeMem;
    char *mpGVMem;

    // GOT Base
    uint8_t *mpGOTBase;

    FunctionMapTy mFunctionMap;


  public:
    CodeMemoryManager();

    virtual ~CodeMemoryManager();

    uint8_t *getCodeMemBase() const {
      return reinterpret_cast<uint8_t*>(mpCodeMem);
    }

    // setMemoryWritable - When code generation is in progress, the code pages
    //                     may need permissions changed.
    virtual void setMemoryWritable();

    // When code generation is done and we're ready to start execution, the
    // code pages may need permissions changed.
    virtual void setMemoryExecutable();

    // 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.
    virtual void setPoisonMemory(bool poison);


    // 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.
    virtual void AllocateGOT();

    // If this is managing a Global Offset Table, this method should return a
    // pointer to its base.
    virtual uint8_t *getGOTBase() const {
      return mpGOTBase;
    }

    // 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.
    virtual uint8_t *startFunctionBody(const llvm::Function *F,
                                       uintptr_t &ActualSize);

    // This method is called by the JIT to allocate space for a function stub
    // (used to handle limited branch displacements) while it is JIT compiling a
    // function. For example, if foo calls bar, and if bar either needs to be
    // lazily compiled or is a native function that exists too far away from the
    // call site to work, this method will be used to make a thunk for it. The
    // stub should be "close" to the current function body, but should not be
    // included in the 'actualsize' returned by startFunctionBody.
    virtual uint8_t *allocateStub(const llvm::GlobalValue *F,
                                  unsigned StubSize,
                                  unsigned Alignment) {
      return allocateSGMemory(StubSize, Alignment);
    }

    // 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.
    virtual void endFunctionBody(const llvm::Function *F,
                                 uint8_t *FunctionStart,
                                 uint8_t *FunctionEnd);

    // Allocate a (function code) memory block of the given size. This method
    // cannot be called between calls to startFunctionBody and endFunctionBody.
    virtual uint8_t *allocateSpace(intptr_t Size, unsigned Alignment);

    // Allocate memory for a global variable.
    virtual uint8_t *allocateGlobal(uintptr_t Size, unsigned Alignment);

    // 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.
    virtual void deallocateFunctionBody(void *Body);

    // When we finished JITing the function, if exception handling is set, we
    // emit the exception table.
    virtual uint8_t *startExceptionTable(const llvm::Function *F,
                                         uintptr_t &ActualSize) {
      bccAssert(false &&
                "Exception is not allowed in our language specification");
      return NULL;
    }

    // This method is called when the JIT is done emitting the exception table.
    virtual void endExceptionTable(const llvm::Function *F, uint8_t *TableStart,
                                   uint8_t *TableEnd, uint8_t *FrameRegister) {
      bccAssert(false &&
                "Exception is not allowed in our language specification");
    }

    // Free the specified exception table's memory. The argument must be the
    // return value from a call to startExceptionTable() that hasn't been
    // deallocated yet. This is never called when the JIT is currently emitting
    // an exception table.
    virtual void deallocateExceptionTable(void *ET) {
      bccAssert(false &&
                "Exception is not allowed in our language specification");
    }

    // Below are the methods we create
    void reset();


  private:
    intptr_t getFreeCodeMemSize() const {
      return mCurSGMemIdx - mCurFuncMemIdx;
    }

    uint8_t *allocateSGMemory(uintptr_t Size,
                              unsigned Alignment = 1 /* no alignment */);

    uintptr_t getFreeGVMemSize() const {
      return MaxGlobalVarSize - mCurGVMemIdx;
    }

    uint8_t *getGVMemBase() const {
      return reinterpret_cast<uint8_t*>(mpGVMem);
    }

  };

} // namespace bcc

#endif  // BCC_CODEMEMORYMANAGER_H