295 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			295 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /**
 | |
|  * vim: set ts=8 sts=2 sw=2 tw=99 et:
 | |
|  * =============================================================================
 | |
|  * SourcePawn JIT SDK
 | |
|  * Copyright (C) 2004-2013 AlliedModders LLC.  All rights reserved.
 | |
|  * =============================================================================
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or modify it under
 | |
|  * the terms of the GNU General Public License, version 3.0, as published by the
 | |
|  * Free Software Foundation.
 | |
|  * 
 | |
|  * This program is distributed in the hope that it will be useful, but WITHOUT
 | |
|  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
|  * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
|  * details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License along with
 | |
|  * this program.  If not, see <http://www.gnu.org/licenses/>.
 | |
|  *
 | |
|  * As a special exception, AlliedModders LLC gives you permission to link the
 | |
|  * code of this program (as well as its derivative works) to "Half-Life 2," the
 | |
|  * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
 | |
|  * by the Valve Corporation.  You must obey the GNU General Public License in
 | |
|  * all respects for all other code used.  Additionally, AlliedModders LLC grants
 | |
|  * this exception to all derivative works.  AlliedModders LLC defines further
 | |
|  * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
 | |
|  * or <http://www.sourcemod.net/license.php>.
 | |
|  *
 | |
|  * Version: $Id$
 | |
|  */
 | |
| #ifndef _include_sourcepawn_assembler_h__
 | |
| #define _include_sourcepawn_assembler_h__
 | |
| 
 | |
| #include <assert.h>
 | |
| #include <stdlib.h>
 | |
| #include <stddef.h>
 | |
| #include <stdint.h>
 | |
| #include <limits.h>
 | |
| 
 | |
| class Assembler
 | |
| {
 | |
|  public:
 | |
|   static const size_t kMinBufferSize = 4096;
 | |
|   static const size_t kMaxInstructionSize = 32;
 | |
|   static const size_t kMaxBufferSize = INT_MAX / 2;
 | |
| 
 | |
|  public:
 | |
|   Assembler() {
 | |
|     buffer_ = (uint8_t *)malloc(kMinBufferSize);
 | |
|     pos_ = buffer_;
 | |
|     end_ = buffer_ + kMinBufferSize;
 | |
|     outOfMemory_ = !buffer_;
 | |
|   }
 | |
|   ~Assembler() {
 | |
|     free(buffer_);
 | |
|   }
 | |
| 
 | |
|   bool outOfMemory() const {
 | |
|     return outOfMemory_;
 | |
|   }
 | |
| 
 | |
|   // Amount needed to allocate for executable code.
 | |
|   size_t length() const {
 | |
|     return pos_ - buffer_;
 | |
|   }
 | |
| 
 | |
|   // Current offset into the code stream.
 | |
|   uint32_t pc() const {
 | |
|     return uint32_t(pos_ - buffer_);
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   void writeByte(uint8_t byte) {
 | |
|     write<uint8_t>(byte);
 | |
|   }
 | |
|   void writeInt32(int32_t word) {
 | |
|     write<int32_t>(word);
 | |
|   }
 | |
|   void writeUint32(uint32_t word) {
 | |
|     write<uint32_t>(word);
 | |
|   }
 | |
|   void writePointer(void *ptr) {
 | |
|     write<void *>(ptr);
 | |
|   }
 | |
| 
 | |
|   template <typename T>
 | |
|   void write(const T &t) {
 | |
|     assertCanWrite(sizeof(T));
 | |
|     *reinterpret_cast<T *>(pos_) = t;
 | |
|     pos_ += sizeof(T);
 | |
|   }
 | |
| 
 | |
|   // Normally this does not need to be checked, but it must be called before
 | |
|   // emitting any instruction.
 | |
|   bool ensureSpace() {
 | |
|     if (pos_ + kMaxInstructionSize <= end_)
 | |
|       return true;
 | |
| 
 | |
|     if (outOfMemory())
 | |
|       return false;
 | |
| 
 | |
|     size_t oldlength = size_t(end_ - buffer_);
 | |
| 
 | |
|     if (oldlength * 2 > kMaxBufferSize) {
 | |
|       // See comment when if realloc() fails.
 | |
|       pos_ = buffer_;
 | |
|       outOfMemory_ = true;
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     size_t oldpos = size_t(pos_ - buffer_);
 | |
|     uint8_t *newbuf = (uint8_t *)realloc(buffer_, oldlength * 2);
 | |
|     if (!newbuf) {
 | |
|       // Writes will be safe, though we'll corrupt the instruction stream, so
 | |
|       // actually using the buffer will be invalid and compilation should be
 | |
|       // aborted when possible.
 | |
|       pos_ = buffer_;
 | |
|       outOfMemory_ = true;
 | |
|       return false;
 | |
|     }
 | |
|     buffer_ = newbuf;
 | |
|     end_ = newbuf + oldlength * 2;
 | |
|     pos_ = buffer_ + oldpos;
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Position will never be negative, but it's nice to have signed results
 | |
|   // for relative address calculation.
 | |
|   int32_t position() const {
 | |
|     return int32_t(pos_ - buffer_);
 | |
|   }
 | |
| 
 | |
|  protected:
 | |
|   void assertCanWrite(size_t bytes) {
 | |
|     assert(pos_ + bytes <= end_);
 | |
|   }
 | |
|   uint8_t *buffer() const {
 | |
|     return buffer_;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   uint8_t *buffer_;
 | |
|   uint8_t *end_;
 | |
| 
 | |
|  protected:
 | |
|   uint8_t *pos_;
 | |
|   bool outOfMemory_;
 | |
| };
 | |
| 
 | |
| class ExternalAddress
 | |
| {
 | |
|  public:
 | |
|   explicit ExternalAddress(void *p)
 | |
|     : p_(p)
 | |
|   {
 | |
|   }
 | |
| 
 | |
|   void *address() const {
 | |
|     return p_;
 | |
|   }
 | |
|   uintptr_t value() const {
 | |
|     return uintptr_t(p_);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   void *p_;
 | |
| };
 | |
| 
 | |
| // A label is a lightweight object to assist in managing relative jumps. It
 | |
| // exists in three states:
 | |
| //   * Unbound, Unused: The label has no incoming jumps, and its position has
 | |
| //     not yet been fixed in the instruction stream.
 | |
| //   * Unbound, Used: The label has not yet been fixed at a position in the
 | |
| //     instruction stream, but it has incoming jumps.
 | |
| //   * Bound: The label has been fixed at a position in the instruction stream.
 | |
| //
 | |
| // When a label is unbound and used, the offset stored in the Label is a linked
 | |
| // list threaded through each individual jump. When the label is bound, each
 | |
| // jump instruction in this list is immediately patched with the correctly
 | |
| // computed relative distance to the label.
 | |
| //
 | |
| // We keep sizeof(Label) == 4 to make it embeddable within code streams if
 | |
| // need be (for example, SourcePawn mirrors the source code to maintain jump
 | |
| // maps).
 | |
| class Label
 | |
| {
 | |
|   // If set on status_, the label is bound.
 | |
|   static const int32_t kBound = (1 << 0);
 | |
| 
 | |
|  public:
 | |
|   Label()
 | |
|    : status_(0)
 | |
|   {
 | |
|   }
 | |
|   ~Label()
 | |
|   {
 | |
|     assert(!used() || bound());
 | |
|   }
 | |
| 
 | |
|   static inline bool More(uint32_t status) {
 | |
|     return status != 0;
 | |
|   }
 | |
|   static inline uint32_t ToOffset(uint32_t status) {
 | |
|     return status >> 1;
 | |
|   }
 | |
| 
 | |
|   bool used() const {
 | |
|     return bound() || !!(status_ >> 1);
 | |
|   }
 | |
|   bool bound() const {
 | |
|     return !!(status_ & kBound);
 | |
|   }
 | |
|   uint32_t offset() const {
 | |
|     assert(bound());
 | |
|     return ToOffset(status_);
 | |
|   }
 | |
|   uint32_t status() const {
 | |
|     assert(!bound());
 | |
|     return status_;
 | |
|   }
 | |
|   uint32_t addPending(uint32_t pc) {
 | |
|     assert(pc <= INT_MAX / 2);
 | |
|     uint32_t prev = status_;
 | |
|     status_ = pc << 1;
 | |
|     return prev;
 | |
|   }
 | |
|   void bind(uint32_t offset) {
 | |
|     assert(!bound());
 | |
|     status_ = (offset << 1) | kBound;
 | |
|     assert(this->offset() == offset);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // Note that 0 as an invalid offset is okay, because the offset we save for
 | |
|   // pending jumps are after the jump opcode itself, and therefore 0 is never
 | |
|   // valid, since there are no 0-byte jumps.
 | |
|   uint32_t status_;
 | |
| };
 | |
| 
 | |
| // A DataLabel is a special form of Label intended for absolute addresses that
 | |
| // are within the code buffer, and thus aren't known yet, and will be
 | |
| // automatically fixed up when calling emitToExecutableMemory().
 | |
| // 
 | |
| // Unlike normal Labels, these do not store a list of incoming uses.
 | |
| class DataLabel
 | |
| {
 | |
|   // If set on status_, the label is bound.
 | |
|   static const int32_t kBound = (1 << 0);
 | |
| 
 | |
|  public:
 | |
|   DataLabel()
 | |
|    : status_(0)
 | |
|   {
 | |
|   }
 | |
|   ~DataLabel()
 | |
|   {
 | |
|     assert(!used() || bound());
 | |
|   }
 | |
| 
 | |
|   static inline uint32_t ToOffset(uint32_t status) {
 | |
|     return status >> 1;
 | |
|   }
 | |
| 
 | |
|   bool used() const {
 | |
|     return bound() || !!(status_ >> 1);
 | |
|   }
 | |
|   bool bound() const {
 | |
|     return !!(status_ & kBound);
 | |
|   }
 | |
|   uint32_t offset() const {
 | |
|     assert(bound());
 | |
|     return ToOffset(status_);
 | |
|   }
 | |
|   uint32_t status() const {
 | |
|     assert(!bound());
 | |
|     return status_;
 | |
|   }
 | |
|   void use(uint32_t pc) {
 | |
|     assert(!used());
 | |
|     status_ = (pc << 1);
 | |
|     assert(ToOffset(status_) == pc);
 | |
|   }
 | |
|   void bind(uint32_t offset) {
 | |
|     assert(!bound());
 | |
|     status_ = (offset << 1) | kBound;
 | |
|     assert(this->offset() == offset);
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   uint32_t status_;
 | |
| };
 | |
| 
 | |
| #endif // _include_sourcepawn_assembler_h__
 | |
| 
 |