Refactor the JIT to use a newer, simpler macro assembler. (bug 5827, r=ann)
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* 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_;
|
||||
}
|
||||
|
||||
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_);
|
||||
}
|
||||
|
||||
// pc is the unsigned version of position().
|
||||
uint32_t pc() const {
|
||||
return uint32_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__
|
||||
|
||||
@@ -0,0 +1,842 @@
|
||||
/**
|
||||
* vim: set ts=8 sts=2 sw=2 tw=99 et:
|
||||
* =============================================================================
|
||||
* SourcePawn JIT SDK
|
||||
* Copyright (C) 2004-2008 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_x86_h__
|
||||
#define _include_sourcepawn_assembler_x86_h__
|
||||
|
||||
#include <assembler.h>
|
||||
#include <ke_vector.h>
|
||||
#include <string.h>
|
||||
|
||||
struct Register
|
||||
{
|
||||
const char *name() const {
|
||||
static const char *names[] = {
|
||||
"eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi"
|
||||
};
|
||||
return names[code];
|
||||
}
|
||||
|
||||
int code;
|
||||
|
||||
bool operator == (const Register &other) const {
|
||||
return code == other.code;
|
||||
}
|
||||
bool operator != (const Register &other) const {
|
||||
return code != other.code;
|
||||
}
|
||||
};
|
||||
|
||||
// X86 has an ancient FPU (called x87) which has a stack of registers
|
||||
// numbered st0 through st7.
|
||||
struct FpuRegister
|
||||
{
|
||||
const char *name() const {
|
||||
static const char *names[] = {
|
||||
"st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7"
|
||||
};
|
||||
return names[code];
|
||||
}
|
||||
|
||||
int code;
|
||||
|
||||
bool operator == (const FpuRegister &other) const {
|
||||
return code == other.code;
|
||||
}
|
||||
bool operator != (const FpuRegister &other) const {
|
||||
return code != other.code;
|
||||
}
|
||||
};
|
||||
|
||||
struct FloatRegister
|
||||
{
|
||||
const char *name() const {
|
||||
static const char *names[] = {
|
||||
"xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
|
||||
};
|
||||
return names[code];
|
||||
}
|
||||
|
||||
int code;
|
||||
|
||||
bool operator == (const FloatRegister &other) const {
|
||||
return code == other.code;
|
||||
}
|
||||
bool operator != (const FloatRegister &other) const {
|
||||
return code != other.code;
|
||||
}
|
||||
};
|
||||
|
||||
const Register eax = { 0 };
|
||||
const Register ecx = { 1 };
|
||||
const Register edx = { 2 };
|
||||
const Register ebx = { 3 };
|
||||
const Register esp = { 4 };
|
||||
const Register ebp = { 5 };
|
||||
const Register esi = { 6 };
|
||||
const Register edi = { 7 };
|
||||
|
||||
const Register r8_al = { 0 };
|
||||
const Register r8_cl = { 1 };
|
||||
const Register r8_dl = { 2 };
|
||||
const Register r8_bl = { 3 };
|
||||
const Register r8_ah = { 4 };
|
||||
const Register r8_ch = { 5 };
|
||||
const Register r8_dh = { 6 };
|
||||
const Register r8_bh = { 7 };
|
||||
|
||||
const FpuRegister st0 = { 0 };
|
||||
const FpuRegister st1 = { 1 };
|
||||
const FpuRegister st2 = { 2 };
|
||||
const FpuRegister st3 = { 3 };
|
||||
const FpuRegister st4 = { 4 };
|
||||
const FpuRegister st5 = { 5 };
|
||||
const FpuRegister st6 = { 6 };
|
||||
const FpuRegister st7 = { 7 };
|
||||
|
||||
const FloatRegister xmm0 = { 0 };
|
||||
const FloatRegister xmm1 = { 1 };
|
||||
const FloatRegister xmm2 = { 2 };
|
||||
const FloatRegister xmm3 = { 3 };
|
||||
const FloatRegister xmm4 = { 4 };
|
||||
const FloatRegister xmm5 = { 5 };
|
||||
const FloatRegister xmm6 = { 6 };
|
||||
const FloatRegister xmm7 = { 7 };
|
||||
|
||||
static const uint8_t kModeDisp0 = 0;
|
||||
static const uint8_t kModeDisp8 = 1;
|
||||
static const uint8_t kModeDisp32 = 2;
|
||||
static const uint8_t kModeReg = 3;
|
||||
static const uint8_t kNoIndex = 4;
|
||||
static const uint8_t kSIB = 4;
|
||||
static const uint8_t kRIP = 5;
|
||||
|
||||
enum ConditionCode {
|
||||
overflow,
|
||||
no_overflow,
|
||||
below,
|
||||
not_below,
|
||||
equal,
|
||||
not_equal,
|
||||
not_above,
|
||||
above,
|
||||
negative,
|
||||
not_negative,
|
||||
even_parity,
|
||||
odd_parity,
|
||||
less,
|
||||
not_less,
|
||||
not_greater,
|
||||
greater,
|
||||
|
||||
zero = equal,
|
||||
not_zero = not_equal,
|
||||
less_equal = not_greater,
|
||||
greater_equal = not_less
|
||||
};
|
||||
|
||||
enum Scale {
|
||||
NoScale,
|
||||
ScaleTwo,
|
||||
ScaleFour,
|
||||
ScaleEight,
|
||||
ScalePointer = ScaleFour
|
||||
};
|
||||
|
||||
struct Operand
|
||||
{
|
||||
friend class AssemblerX86;
|
||||
|
||||
public:
|
||||
Operand(Register reg, int32_t disp) {
|
||||
if (reg == esp) {
|
||||
// If the reg is esp, we need a SIB encoding.
|
||||
if (disp == 0)
|
||||
sib_disp0(NoScale, kNoIndex, reg.code);
|
||||
else if (disp >= SCHAR_MIN && disp <= SCHAR_MAX)
|
||||
sib_disp8(NoScale, kNoIndex, reg.code, disp);
|
||||
else
|
||||
sib_disp32(NoScale, kNoIndex, reg.code, disp);
|
||||
} else if (disp == 0 && reg != ebp) {
|
||||
// note, [ebp+0] is disp32/rip
|
||||
modrm_disp0(reg.code);
|
||||
} else if (disp >= SCHAR_MIN && disp <= SCHAR_MAX) {
|
||||
modrm_disp8(reg.code, disp);
|
||||
} else {
|
||||
modrm_disp32(reg.code, disp);
|
||||
}
|
||||
}
|
||||
|
||||
Operand(Register base, Scale scale, int32_t disp = 0) {
|
||||
if (disp == 0 && base != ebp)
|
||||
sib_disp0(scale, kNoIndex, base.code);
|
||||
else if (disp >= SCHAR_MIN && disp <= SCHAR_MAX)
|
||||
sib_disp8(scale, kNoIndex, base.code, disp);
|
||||
else
|
||||
sib_disp32(scale, kNoIndex, base.code, disp);
|
||||
}
|
||||
|
||||
Operand(Register base, Register index, Scale scale, int32_t disp = 0) {
|
||||
assert(index.code != kNoIndex);
|
||||
if (disp == 0 && base != ebp)
|
||||
sib_disp0(scale, index.code, base.code);
|
||||
else if (disp >= SCHAR_MIN && disp <= SCHAR_MAX)
|
||||
sib_disp8(scale, index.code, base.code, disp);
|
||||
else
|
||||
sib_disp32(scale, index.code, base.code, disp);
|
||||
}
|
||||
|
||||
explicit Operand(ExternalAddress address) {
|
||||
modrm(kModeDisp0, kRIP);
|
||||
*reinterpret_cast<const void **>(bytes_ + 1) = address.address();
|
||||
}
|
||||
|
||||
bool isRegister() const {
|
||||
return mode() == kModeReg;
|
||||
}
|
||||
bool isRegister(Register r) const {
|
||||
return mode() == kModeReg && rm() == r.code;
|
||||
}
|
||||
int registerCode() const {
|
||||
return rm();
|
||||
}
|
||||
|
||||
uint8_t getByte(size_t index) const {
|
||||
assert(index < length());
|
||||
return bytes_[index];
|
||||
}
|
||||
|
||||
size_t length() const {
|
||||
if (mode() == kModeDisp0 && rm() == kRIP)
|
||||
return 5;
|
||||
size_t sib = (mode() != kModeReg && rm() == kSIB);
|
||||
if (mode() == kModeDisp32)
|
||||
return 5 + sib;
|
||||
if (mode() == kModeDisp8)
|
||||
return 2 + sib;
|
||||
return 1 + sib;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Operand(Register reg) {
|
||||
modrm(kModeReg, reg.code);
|
||||
}
|
||||
|
||||
void modrm(uint8_t mode, uint8_t rm) {
|
||||
assert(mode <= 3);
|
||||
assert(rm <= 7);
|
||||
bytes_[0] = (mode << 6) | rm;
|
||||
}
|
||||
void modrm_disp0(uint8_t rm) {
|
||||
modrm(kModeDisp0, rm);
|
||||
}
|
||||
void modrm_disp8(uint8_t rm, int8_t disp) {
|
||||
modrm(kModeDisp8, rm);
|
||||
bytes_[1] = disp;
|
||||
}
|
||||
void modrm_disp32(uint8_t rm, int32_t disp) {
|
||||
modrm(kModeDisp32, rm);
|
||||
*reinterpret_cast<int32_t *>(bytes_ + 1) = disp;
|
||||
}
|
||||
void sib(uint8_t mode, Scale scale, uint8_t index, uint8_t base) {
|
||||
modrm(mode, kSIB);
|
||||
|
||||
assert(scale <= 3);
|
||||
assert(index <= 7);
|
||||
assert(base <= 7);
|
||||
bytes_[1] = (uint8_t(scale) << 6) | (index << 3) | base;
|
||||
}
|
||||
void sib_disp0(Scale scale, uint8_t index, uint8_t base) {
|
||||
sib(kModeDisp0, scale, index, base);
|
||||
}
|
||||
void sib_disp8(Scale scale, uint8_t index, uint8_t base, int8_t disp) {
|
||||
sib(kModeDisp8, scale, index, base);
|
||||
bytes_[2] = disp;
|
||||
}
|
||||
void sib_disp32(Scale scale, uint8_t index, uint8_t base, int32_t disp) {
|
||||
sib(kModeDisp32, scale, index, base);
|
||||
*reinterpret_cast<int32_t *>(bytes_ + 2) = disp;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t rm() const {
|
||||
return bytes_[0] & 7;
|
||||
}
|
||||
uint8_t mode() const {
|
||||
return bytes_[0] >> 6;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t bytes_[6];
|
||||
};
|
||||
|
||||
class AssemblerX86 : public Assembler
|
||||
{
|
||||
public:
|
||||
void movl(Register dest, Register src) {
|
||||
emit1(0x89, src.code, dest.code);
|
||||
}
|
||||
void movl(Register dest, const Operand &src) {
|
||||
emit1(0x8b, dest.code, src);
|
||||
}
|
||||
void movl(const Operand &dest, Register src) {
|
||||
emit1(0x89, src.code, dest);
|
||||
}
|
||||
void movl(Register dest, int32_t imm) {
|
||||
emit1(0xb8 + dest.code);
|
||||
writeInt32(imm);
|
||||
}
|
||||
void movl(const Operand &dest, int32_t imm) {
|
||||
if (dest.isRegister())
|
||||
emit1(0xb8 + dest.registerCode());
|
||||
else
|
||||
emit1(0xc7, 0, dest);
|
||||
writeInt32(imm);
|
||||
}
|
||||
void movw(const Operand &dest, Register src) {
|
||||
emit1(0x89, src.code, dest);
|
||||
}
|
||||
void movw(Register dest, const Operand &src) {
|
||||
emit1(0x8b, dest.code, src);
|
||||
}
|
||||
void movb(const Operand &dest, Register src) {
|
||||
emit1(0x88, src.code, dest);
|
||||
}
|
||||
void movb(Register dest, const Operand &src) {
|
||||
emit1(0x8a, dest.code, src);
|
||||
}
|
||||
void movzxb(Register dest, const Operand &src) {
|
||||
emit2(0x0f, 0xb6, dest.code, src);
|
||||
}
|
||||
void movzxb(Register dest, const Register src) {
|
||||
emit2(0x0f, 0xb6, dest.code, src.code);
|
||||
}
|
||||
void movzxw(Register dest, const Operand &src) {
|
||||
emit2(0x0f, 0xb7, dest.code, src);
|
||||
}
|
||||
void movzxw(Register dest, const Register src) {
|
||||
emit2(0x0f, 0xb7, dest.code, src.code);
|
||||
}
|
||||
|
||||
void lea(Register dest, const Operand &src) {
|
||||
emit1(0x8d, dest.code, src);
|
||||
}
|
||||
|
||||
void xchgl(Register dest, Register src) {
|
||||
if (src == eax)
|
||||
emit1(0x90 + dest.code);
|
||||
else if (dest == eax)
|
||||
emit1(0x90 + src.code);
|
||||
else
|
||||
emit1(0x87, src.code, dest.code);
|
||||
}
|
||||
|
||||
void shll_cl(Register dest) {
|
||||
shift_cl(dest.code, 4);
|
||||
}
|
||||
void shll(Register dest, uint8_t imm) {
|
||||
shift_imm(dest.code, 4, imm);
|
||||
}
|
||||
void shll(const Operand &dest, uint8_t imm) {
|
||||
shift_imm(dest, 4, imm);
|
||||
}
|
||||
void shrl_cl(Register dest) {
|
||||
shift_cl(dest.code, 5);
|
||||
}
|
||||
void shrl(Register dest, uint8_t imm) {
|
||||
shift_imm(dest.code, 5, imm);
|
||||
}
|
||||
void shrl(const Operand &dest, uint8_t imm) {
|
||||
shift_imm(dest, 5, imm);
|
||||
}
|
||||
void sarl_cl(Register dest) {
|
||||
shift_cl(dest.code, 7);
|
||||
}
|
||||
void sarl(Register dest, uint8_t imm) {
|
||||
shift_imm(dest.code, 7, imm);
|
||||
}
|
||||
void sarl(const Operand &dest, uint8_t imm) {
|
||||
shift_imm(dest, 7, imm);
|
||||
}
|
||||
|
||||
void cmpl(Register left, int32_t imm) {
|
||||
alu_imm(7, imm, Operand(left));
|
||||
}
|
||||
void cmpl(const Operand &left, int32_t imm) {
|
||||
alu_imm(7, imm, left);
|
||||
}
|
||||
void cmpl(Register left, Register right) {
|
||||
emit1(0x39, right.code, left.code);
|
||||
}
|
||||
void cmpl(const Operand &left, Register right) {
|
||||
emit1(0x39, right.code, left);
|
||||
}
|
||||
void cmpl(Register left, const Operand &right) {
|
||||
emit1(0x3b, left.code, right);
|
||||
}
|
||||
void andl(Register dest, int32_t imm) {
|
||||
alu_imm(4, imm, Operand(dest));
|
||||
}
|
||||
void andl(const Operand &dest, int32_t imm) {
|
||||
alu_imm(4, imm, dest);
|
||||
}
|
||||
void andl(Register dest, Register src) {
|
||||
emit1(0x21, src.code, dest.code);
|
||||
}
|
||||
void andl(const Operand &dest, Register src) {
|
||||
emit1(0x21, src.code, dest);
|
||||
}
|
||||
void andl(Register dest, const Operand &src) {
|
||||
emit1(0x23, dest.code, src);
|
||||
}
|
||||
void orl(Register dest, Register src) {
|
||||
emit1(0x09, src.code, dest.code);
|
||||
}
|
||||
void orl(const Operand &dest, Register src) {
|
||||
emit1(0x09, src.code, dest);
|
||||
}
|
||||
void orl(Register dest, const Operand &src) {
|
||||
emit1(0x0b, dest.code, src);
|
||||
}
|
||||
void xorl(Register dest, Register src) {
|
||||
emit1(0x31, src.code, dest.code);
|
||||
}
|
||||
void xorl(const Operand &dest, Register src) {
|
||||
emit1(0x31, src.code, dest);
|
||||
}
|
||||
void xorl(Register dest, const Operand &src) {
|
||||
emit1(0x33, dest.code, src);
|
||||
}
|
||||
|
||||
void subl(Register dest, Register src) {
|
||||
emit1(0x29, src.code, dest.code);
|
||||
}
|
||||
void subl(const Operand &dest, Register src) {
|
||||
emit1(0x29, src.code, dest);
|
||||
}
|
||||
void subl(Register dest, const Operand &src) {
|
||||
emit1(0x2b, dest.code, src);
|
||||
}
|
||||
void subl(Register dest, int32_t imm) {
|
||||
alu_imm(5, imm, Operand(dest));
|
||||
}
|
||||
void subl(const Operand &dest, int32_t imm) {
|
||||
alu_imm(5, imm, dest);
|
||||
}
|
||||
void addl(Register dest, Register src) {
|
||||
emit1(0x01, src.code, dest.code);
|
||||
}
|
||||
void addl(const Operand &dest, Register src) {
|
||||
emit1(0x01, src.code, dest);
|
||||
}
|
||||
void addl(Register dest, const Operand &src) {
|
||||
emit1(0x03, dest.code, src);
|
||||
}
|
||||
void addl(Register dest, int32_t imm) {
|
||||
alu_imm(0, imm, Operand(dest));
|
||||
}
|
||||
void addl(const Operand &dest, int32_t imm) {
|
||||
alu_imm(0, imm, dest);
|
||||
}
|
||||
|
||||
void imull(Register dest, const Operand &src) {
|
||||
emit2(0x0f, 0xaf, dest.code, src);
|
||||
}
|
||||
void imull(Register dest, Register src) {
|
||||
emit2(0x0f, 0xaf, dest.code, src.code);
|
||||
}
|
||||
void imull(Register dest, const Operand &src, int32_t imm) {
|
||||
if (imm >= SCHAR_MIN && imm <= SCHAR_MAX) {
|
||||
emit1(0x6b, dest.code, src);
|
||||
*pos_++ = imm;
|
||||
} else {
|
||||
emit1(0x69, dest.code, src);
|
||||
writeInt32(imm);
|
||||
}
|
||||
}
|
||||
void imull(Register dest, Register src, int32_t imm) {
|
||||
imull(dest, Operand(src), imm);
|
||||
}
|
||||
|
||||
void testl(const Operand &op1, Register op2) {
|
||||
emit1(0x85, op2.code, op1);
|
||||
}
|
||||
void testl(Register op1, Register op2) {
|
||||
emit1(0x85, op2.code, op1.code);
|
||||
}
|
||||
void set(ConditionCode cc, const Operand &dest) {
|
||||
emit2(0x0f, 0x90 + uint8_t(cc), 0, dest);
|
||||
}
|
||||
void set(ConditionCode cc, Register dest) {
|
||||
emit2(0x0f, 0x90 + uint8_t(cc), 0, dest.code);
|
||||
}
|
||||
void negl(Register srcdest) {
|
||||
emit1(0xf7, 3, srcdest.code);
|
||||
}
|
||||
void negl(const Operand &srcdest) {
|
||||
emit1(0xf7, 3, srcdest);
|
||||
}
|
||||
void notl(Register srcdest) {
|
||||
emit1(0xf7, 2, srcdest.code);
|
||||
}
|
||||
void notl(const Operand &srcdest) {
|
||||
emit1(0xf7, 2, srcdest);
|
||||
}
|
||||
void idivl(Register dividend) {
|
||||
emit1(0xf7, 7, dividend.code);
|
||||
}
|
||||
void idivl(const Operand ÷nd) {
|
||||
emit1(0xf7, 7, dividend);
|
||||
}
|
||||
|
||||
void ret() {
|
||||
emit1(0xc3);
|
||||
}
|
||||
void cld() {
|
||||
emit1(0xfc);
|
||||
}
|
||||
void push(Register reg) {
|
||||
emit1(0x50 + reg.code);
|
||||
}
|
||||
void push(const Operand &src) {
|
||||
if (src.isRegister())
|
||||
emit1(0x50 + src.registerCode());
|
||||
else
|
||||
emit1(0xff, 6, src);
|
||||
}
|
||||
void push(int32_t imm) {
|
||||
emit1(0x68);
|
||||
writeInt32(imm);
|
||||
}
|
||||
void pop(Register reg) {
|
||||
emit1(0x58 + reg.code);
|
||||
}
|
||||
void pop(const Operand &src) {
|
||||
if (src.isRegister())
|
||||
emit1(0x58 + src.registerCode());
|
||||
else
|
||||
emit1(0x8f, 0, src);
|
||||
}
|
||||
|
||||
void rep_movsb() {
|
||||
emit2(0xf3, 0xa4);
|
||||
}
|
||||
void rep_movsd() {
|
||||
emit2(0xf3, 0xa5);
|
||||
}
|
||||
void rep_stosd() {
|
||||
emit2(0xf3, 0xab);
|
||||
}
|
||||
void breakpoint() {
|
||||
emit1(0xcc);
|
||||
}
|
||||
|
||||
void fld32(const Operand &src) {
|
||||
emit1(0xd9, 0, src);
|
||||
}
|
||||
void fild32(const Operand &src) {
|
||||
emit1(0xdb, 0, src);
|
||||
}
|
||||
void fistp32(const Operand &dest) {
|
||||
emit1(0xdb, 3, dest);
|
||||
}
|
||||
void fadd32(const Operand &src) {
|
||||
emit1(0xd8, 0, src);
|
||||
}
|
||||
void fsub32(const Operand &src) {
|
||||
emit1(0xd8, 4, src);
|
||||
}
|
||||
void fmul32(const Operand &src) {
|
||||
emit1(0xd8, 1, src);
|
||||
}
|
||||
void fdiv32(const Operand &src) {
|
||||
emit1(0xd8, 6, src);
|
||||
}
|
||||
void fstp32(const Operand &dest) {
|
||||
emit1(0xd9, 3, dest);
|
||||
}
|
||||
void fstp(FpuRegister src) {
|
||||
emit2(0xdd, 0xd8 + src.code);
|
||||
}
|
||||
void fldcw(const Operand &src) {
|
||||
emit1(0xd9, 5, src);
|
||||
}
|
||||
void fstcw(const Operand &dest) {
|
||||
emit2(0x9b, 0xd9, 7, dest);
|
||||
}
|
||||
void fsubr32(const Operand &src) {
|
||||
emit1(0xd8, 5, src);
|
||||
}
|
||||
|
||||
// Compare st0 with stN.
|
||||
void fucomip(FpuRegister other) {
|
||||
emit2(0xdf, 0xe8 + other.code);
|
||||
}
|
||||
|
||||
// At least one argument of these forms must be st0.
|
||||
void fadd32(FpuRegister dest, FpuRegister src) {
|
||||
assert(dest == st0 || src == st0);
|
||||
if (dest == st0)
|
||||
emit2(0xd8, 0xc0 + dest.code);
|
||||
else
|
||||
emit2(0xdc, 0xc0 + src.code);
|
||||
}
|
||||
|
||||
void jmp(Label *dest) {
|
||||
int8_t d8;
|
||||
if (canEmitSmallJump(dest, &d8)) {
|
||||
emit2(0xeb, d8);
|
||||
} else {
|
||||
emit1(0xe9);
|
||||
emitJumpTarget(dest);
|
||||
}
|
||||
}
|
||||
void jmp(Register target) {
|
||||
emit1(0xff, 4, target.code);
|
||||
}
|
||||
void jmp(const Operand &target) {
|
||||
emit1(0xff, 4, target);
|
||||
}
|
||||
void j(ConditionCode cc, Label *dest) {
|
||||
int8_t d8;
|
||||
if (canEmitSmallJump(dest, &d8)) {
|
||||
emit2(0x70 + uint8_t(cc), d8);
|
||||
} else {
|
||||
emit2(0x0f, 0x80 + uint8_t(cc));
|
||||
emitJumpTarget(dest);
|
||||
}
|
||||
}
|
||||
void call(Label *dest) {
|
||||
emit1(0xe8);
|
||||
emitJumpTarget(dest);
|
||||
}
|
||||
void bind(Label *target) {
|
||||
if (outOfMemory()) {
|
||||
// If we ran out of memory, the code stream is potentially invalid and
|
||||
// we cannot use the embedded linked list.
|
||||
target->bind(pc());
|
||||
return;
|
||||
}
|
||||
|
||||
assert(!target->bound());
|
||||
uint32_t status = target->status();
|
||||
while (Label::More(status)) {
|
||||
// Grab the offset. It should be at least a 1byte op + rel32.
|
||||
uint32_t offset = Label::ToOffset(status);
|
||||
assert(offset >= 5);
|
||||
|
||||
// Grab the delta from target to pc.
|
||||
ptrdiff_t delta = pos_ - (buffer() + offset);
|
||||
assert(delta >= INT_MIN && delta <= INT_MAX);
|
||||
|
||||
int32_t *p = reinterpret_cast<int32_t *>(buffer() + offset - 4);
|
||||
status = *p;
|
||||
*p = delta;
|
||||
}
|
||||
target->bind(pc());
|
||||
}
|
||||
|
||||
void bind(DataLabel *address) {
|
||||
if (outOfMemory())
|
||||
return;
|
||||
if (address->used()) {
|
||||
uint32_t offset = DataLabel::ToOffset(address->status());
|
||||
*reinterpret_cast<int32_t *>(buffer() + offset - 4) = position() - int32_t(offset);
|
||||
}
|
||||
address->bind(pc());
|
||||
}
|
||||
void movl(Register dest, DataLabel *src) {
|
||||
emit1(0xb8 + dest.code);
|
||||
if (src->bound()) {
|
||||
writeInt32(int32_t(src->offset()) - (position() + 4));
|
||||
} else {
|
||||
writeInt32(0xabcdef0);
|
||||
src->use(pc());
|
||||
}
|
||||
if (!local_refs_.append(pc()))
|
||||
outOfMemory_ = true;
|
||||
}
|
||||
void emit_absolute_address(Label *address) {
|
||||
if (address->bound())
|
||||
writeUint32(int32_t(address->offset()) - (position() + 4));
|
||||
else
|
||||
writeUint32(address->addPending(position() + 4));
|
||||
if (!local_refs_.append(pc()))
|
||||
outOfMemory_ = true;
|
||||
}
|
||||
|
||||
void call(Register target) {
|
||||
emit1(0xff, 2, target.code);
|
||||
}
|
||||
void call(const Operand &target) {
|
||||
emit1(0xff, 2, target);
|
||||
}
|
||||
void call(ExternalAddress address) {
|
||||
emit1(0xe8);
|
||||
writeInt32(address.value());
|
||||
if (!external_refs_.append(pc()))
|
||||
outOfMemory_ = true;
|
||||
}
|
||||
void jmp(ExternalAddress address) {
|
||||
assert(sizeof(address) == sizeof(int32_t));
|
||||
emit1(0xe9);
|
||||
writeInt32(address.value());
|
||||
if (!external_refs_.append(pc()))
|
||||
outOfMemory_ = true;
|
||||
}
|
||||
|
||||
static void PatchRel32Absolute(uint8_t *ip, void *ptr) {
|
||||
int32_t delta = uint32_t(ptr) - uint32_t(ip);
|
||||
*reinterpret_cast<int32_t *>(ip - 4) = delta;
|
||||
}
|
||||
|
||||
void emitToExecutableMemory(void *code) {
|
||||
assert(!outOfMemory());
|
||||
|
||||
// Relocate anything we emitted as rel32 with an external pointer.
|
||||
uint8_t *base = reinterpret_cast<uint8_t *>(code);
|
||||
memcpy(base, buffer(), length());
|
||||
for (size_t i = 0; i < external_refs_.length(); i++) {
|
||||
size_t offset = external_refs_[i];
|
||||
PatchRel32Absolute(base + offset, *reinterpret_cast<void **>(base + offset - 4));
|
||||
}
|
||||
|
||||
// Relocate everything we emitted as an abs32 with an internal offset. Note
|
||||
// that in the code stream, we use relative offsets so we can use both Label
|
||||
// and DataLabel.
|
||||
for (size_t i = 0; i < local_refs_.length(); i++) {
|
||||
size_t offset = local_refs_[i];
|
||||
int32_t delta = *reinterpret_cast<int32_t *>(base + offset - 4);
|
||||
*reinterpret_cast<void **>(base + offset - 4) = base + offset + delta;
|
||||
}
|
||||
}
|
||||
|
||||
void align(uint32_t bytes) {
|
||||
int32_t delta = (pc() & ~(bytes - 1)) + bytes - pc();
|
||||
for (int32_t i = 0; i < delta; i++)
|
||||
emit1(0xcc);
|
||||
}
|
||||
|
||||
private:
|
||||
bool canEmitSmallJump(Label *dest, int8_t *deltap) {
|
||||
if (!dest->bound())
|
||||
return false;
|
||||
|
||||
// All small jumps are assumed to be 2 bytes.
|
||||
ptrdiff_t delta = ptrdiff_t(dest->offset()) - (position() + 2);
|
||||
if (delta < SCHAR_MIN || delta > SCHAR_MAX)
|
||||
return false;
|
||||
*deltap = delta;
|
||||
return true;
|
||||
}
|
||||
void emitJumpTarget(Label *dest) {
|
||||
if (dest->bound()) {
|
||||
ptrdiff_t delta = ptrdiff_t(dest->offset()) - (position() + 4);
|
||||
assert(delta >= INT_MIN && delta <= INT_MAX);
|
||||
writeInt32(delta);
|
||||
} else {
|
||||
writeUint32(dest->addPending(position() + 4));
|
||||
}
|
||||
}
|
||||
|
||||
void emit(uint8_t reg, const Operand &operand) {
|
||||
*pos_++ = operand.getByte(0) | (reg << 3);
|
||||
size_t length = operand.length();
|
||||
for (size_t i = 1; i < length; i++)
|
||||
*pos_++ = operand.getByte(i);
|
||||
}
|
||||
|
||||
void emit1(uint8_t opcode) {
|
||||
ensureSpace();
|
||||
*pos_++ = opcode;
|
||||
}
|
||||
void emit1(uint8_t opcode, uint8_t reg, uint8_t opreg) {
|
||||
ensureSpace();
|
||||
assert(reg <= 7);
|
||||
assert(opreg <= 7);
|
||||
*pos_++ = opcode;
|
||||
*pos_++ = (kModeReg << 6) | (reg << 3) | opreg;
|
||||
}
|
||||
void emit1(uint8_t opcode, uint8_t reg, const Operand &operand) {
|
||||
ensureSpace();
|
||||
assert(reg <= 7);
|
||||
*pos_++ = opcode;
|
||||
emit(reg, operand);
|
||||
}
|
||||
|
||||
void emit2(uint8_t prefix, uint8_t opcode) {
|
||||
ensureSpace();
|
||||
*pos_++ = prefix;
|
||||
*pos_++ = opcode;
|
||||
}
|
||||
void emit2(uint8_t prefix, uint8_t opcode, uint8_t reg, uint8_t opreg) {
|
||||
emit2(prefix, opcode);
|
||||
assert(reg <= 7);
|
||||
*pos_++ = (kModeReg << 6) | (reg << 3) | opreg;
|
||||
}
|
||||
void emit2(uint8_t prefix, uint8_t opcode, uint8_t reg, const Operand &operand) {
|
||||
emit2(prefix, opcode);
|
||||
emit(reg, operand);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void shift_cl(const T &t, uint8_t r) {
|
||||
emit1(0xd3, r, t);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void shift_imm(const T &t, uint8_t r, int32_t imm) {
|
||||
if (imm == 1) {
|
||||
emit1(0xd1, r, t);
|
||||
} else {
|
||||
emit1(0xc1, r, t);
|
||||
*pos_++ = imm & 0x1F;
|
||||
}
|
||||
}
|
||||
void alu_imm(uint8_t r, int32_t imm, const Operand &operand) {
|
||||
if (imm >= SCHAR_MIN && imm <= SCHAR_MAX) {
|
||||
emit1(0x83, r, operand);
|
||||
*pos_++ = uint8_t(imm & 0xff);
|
||||
} else if (operand.isRegister(eax)) {
|
||||
emit1(0x05 | (r << 3));
|
||||
writeInt32(imm);
|
||||
} else {
|
||||
emit1(0x81, r, operand);
|
||||
writeInt32(imm);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ke::Vector<uint32_t> external_refs_;
|
||||
ke::Vector<uint32_t> local_refs_;
|
||||
};
|
||||
|
||||
#endif // _include_sourcepawn_assembler_x86_h__
|
||||
|
||||
+207
-1302
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
/* vim: set ts=2 sw=2 tw=99 et:
|
||||
*
|
||||
* Copyright (C) 2012 David Anderson
|
||||
*
|
||||
* This file is part of SourcePawn.
|
||||
*
|
||||
* SourcePawn is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* SourcePawn 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
|
||||
* SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
#ifndef _include_sourcepawn_allocatorpolicies_h_
|
||||
#define _include_sourcepawn_allocatorpolicies_h_
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
namespace ke {
|
||||
|
||||
class SystemAllocatorPolicy
|
||||
{
|
||||
protected:
|
||||
void reportOutOfMemory() {
|
||||
fprintf(stderr, "OUT OF MEMORY\n");
|
||||
abort();
|
||||
}
|
||||
void reportAllocationOverflow() {
|
||||
fprintf(stderr, "OUT OF MEMORY\n");
|
||||
abort();
|
||||
}
|
||||
|
||||
public:
|
||||
void free(void *memory) {
|
||||
::free(memory);
|
||||
}
|
||||
void *malloc(size_t bytes) {
|
||||
void *ptr = ::malloc(bytes);
|
||||
if (!ptr)
|
||||
reportOutOfMemory();
|
||||
return ptr;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // _include_sourcepawn_allocatorpolicies_h_
|
||||
@@ -0,0 +1,318 @@
|
||||
/* vim: set ts=4 sw=4 tw=99 et:
|
||||
*
|
||||
* Copyright (C) 2012-2013 David Anderson
|
||||
*
|
||||
* This file is part of SourcePawn.
|
||||
*
|
||||
* SourcePawn is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* SourcePawn 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
|
||||
* SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
#ifndef _include_jitcraft_utility_h_
|
||||
#define _include_jitcraft_utility_h_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#if defined(_MSC_VER)
|
||||
# include <intrin.h>
|
||||
#endif
|
||||
|
||||
#define KE_32BIT
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# pragma warning(disable:4355)
|
||||
#endif
|
||||
|
||||
namespace ke {
|
||||
|
||||
static const size_t kMallocAlignment = sizeof(void *) * 2;
|
||||
|
||||
typedef uint8_t uint8;
|
||||
typedef int32_t int32;
|
||||
typedef uint32_t uint32;
|
||||
typedef int64_t int64;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
typedef uint8 * Address;
|
||||
|
||||
static const size_t kKB = 1024;
|
||||
static const size_t kMB = 1024 * kKB;
|
||||
static const size_t kGB = 1024 * kMB;
|
||||
|
||||
template <typename T>
|
||||
class AutoFree
|
||||
{
|
||||
T *t_;
|
||||
|
||||
public:
|
||||
AutoFree()
|
||||
: t_(NULL)
|
||||
{
|
||||
}
|
||||
AutoFree(T *t)
|
||||
: t_(t)
|
||||
{
|
||||
}
|
||||
~AutoFree() {
|
||||
free(t_);
|
||||
}
|
||||
T *take() {
|
||||
T *t = t_;
|
||||
t_ = NULL;
|
||||
return t;
|
||||
}
|
||||
T *operator *() const {
|
||||
return t_;
|
||||
}
|
||||
void operator =(T *t) {
|
||||
if (t_)
|
||||
free(t_);
|
||||
t_ = t;
|
||||
}
|
||||
};
|
||||
|
||||
// Bob Jenkin's one-at-a-time hash function[1].
|
||||
//
|
||||
// [1] http://burtleburtle.net/bob/hash/doobs.html
|
||||
class CharacterStreamHasher
|
||||
{
|
||||
uint32 hash;
|
||||
|
||||
public:
|
||||
CharacterStreamHasher()
|
||||
: hash(0)
|
||||
{ }
|
||||
|
||||
void add(char c) {
|
||||
hash += c;
|
||||
hash += (hash << 10);
|
||||
hash ^= (hash >> 6);
|
||||
}
|
||||
|
||||
void add(const char *s, size_t length) {
|
||||
for (size_t i = 0; i < length; i++)
|
||||
add(s[i]);
|
||||
}
|
||||
|
||||
uint32 result() {
|
||||
hash += (hash << 3);
|
||||
hash ^= (hash >> 11);
|
||||
hash += (hash << 15);
|
||||
return hash;
|
||||
}
|
||||
};
|
||||
|
||||
static inline uint32
|
||||
HashCharSequence(const char *s, size_t length)
|
||||
{
|
||||
CharacterStreamHasher hasher;
|
||||
hasher.add(s, length);
|
||||
return hasher.result();
|
||||
}
|
||||
|
||||
// From http://burtleburtle.net/bob/hash/integer.html
|
||||
static inline uint32
|
||||
HashInt32(int32 a)
|
||||
{
|
||||
a = (a ^ 61) ^ (a >> 16);
|
||||
a = a + (a << 3);
|
||||
a = a ^ (a >> 4);
|
||||
a = a * 0x27d4eb2d;
|
||||
a = a ^ (a >> 15);
|
||||
return a;
|
||||
}
|
||||
|
||||
// From http://www.cris.com/~Ttwang/tech/inthash.htm
|
||||
static inline uint32
|
||||
HashInt64(int64 key)
|
||||
{
|
||||
key = (~key) + (key << 18); // key = (key << 18) - key - 1;
|
||||
key = key ^ (uint64(key) >> 31);
|
||||
key = key * 21; // key = (key + (key << 2)) + (key << 4);
|
||||
key = key ^ (uint64(key) >> 11);
|
||||
key = key + (key << 6);
|
||||
key = key ^ (uint64(key) >> 22);
|
||||
return uint32(key);
|
||||
}
|
||||
|
||||
static inline uint32
|
||||
HashPointer(void *p)
|
||||
{
|
||||
#if defined(KE_32BIT)
|
||||
return HashInt32(reinterpret_cast<int32>(p));
|
||||
#elif defined(KE_64BIT)
|
||||
return HashInt64(reinterpret_cast<int64>(p));
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
Log2(size_t number)
|
||||
{
|
||||
assert(number != 0);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
unsigned long rval;
|
||||
# ifdef _M_IX86
|
||||
_BitScanReverse(&rval, number);
|
||||
# elif _M_X64
|
||||
_BitScanReverse64(&rval, number);
|
||||
# endif
|
||||
return rval;
|
||||
#else
|
||||
size_t bit;
|
||||
asm("bsr %1, %0\n"
|
||||
: "=r" (bit)
|
||||
: "rm" (number));
|
||||
return bit;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
FindRightmostBit(size_t number)
|
||||
{
|
||||
assert(number != 0);
|
||||
|
||||
#ifdef _MSC_VER
|
||||
unsigned long rval;
|
||||
# ifdef _M_IX86
|
||||
_BitScanForward(&rval, number);
|
||||
# elif _M_X64
|
||||
_BitScanForward64(&rval, number);
|
||||
# endif
|
||||
return rval;
|
||||
#else
|
||||
size_t bit;
|
||||
asm("bsf %1, %0\n"
|
||||
: "=r" (bit)
|
||||
: "rm" (number));
|
||||
return bit;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsPowerOfTwo(size_t value)
|
||||
{
|
||||
if (value == 0)
|
||||
return false;
|
||||
return !(value & (value - 1));
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
Align(size_t count, size_t alignment)
|
||||
{
|
||||
assert(IsPowerOfTwo(alignment));
|
||||
return count + (alignment - (count % alignment)) % alignment;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsUint32AddSafe(unsigned a, unsigned b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return true;
|
||||
size_t log2_a = Log2(a);
|
||||
size_t log2_b = Log2(b);
|
||||
return (log2_a < sizeof(unsigned) * 8) &&
|
||||
(log2_b < sizeof(unsigned) * 8);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsUintPtrAddSafe(size_t a, size_t b)
|
||||
{
|
||||
if (!a || !b)
|
||||
return true;
|
||||
size_t log2_a = Log2(a);
|
||||
size_t log2_b = Log2(b);
|
||||
return (log2_a < sizeof(size_t) * 8) &&
|
||||
(log2_b < sizeof(size_t) * 8);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsUint32MultiplySafe(unsigned a, unsigned b)
|
||||
{
|
||||
if (a <= 1 || b <= 1)
|
||||
return true;
|
||||
|
||||
size_t log2_a = Log2(a);
|
||||
size_t log2_b = Log2(b);
|
||||
return log2_a + log2_b <= sizeof(unsigned) * 8;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsUintPtrMultiplySafe(size_t a, size_t b)
|
||||
{
|
||||
if (a <= 1 || b <= 1)
|
||||
return true;
|
||||
|
||||
size_t log2_a = Log2(a);
|
||||
size_t log2_b = Log2(b);
|
||||
return log2_a + log2_b <= sizeof(size_t) * 8;
|
||||
}
|
||||
|
||||
#define ARRAY_LENGTH(array) (sizeof(array) / sizeof(array[0]))
|
||||
#define STATIC_ASSERT(cond) extern int static_assert_f(int a[(cond) ? 1 : -1])
|
||||
|
||||
#define IS_ALIGNED(addr, alignment) (!(uintptr_t(addr) & ((alignment) - 1)))
|
||||
|
||||
template <typename T>
|
||||
static inline bool
|
||||
IsAligned(T addr, size_t alignment)
|
||||
{
|
||||
assert(IsPowerOfTwo(alignment));
|
||||
return !(uintptr_t(addr) & (alignment - 1));
|
||||
}
|
||||
|
||||
static inline Address
|
||||
AlignedBase(Address addr, size_t alignment)
|
||||
{
|
||||
assert(IsPowerOfTwo(alignment));
|
||||
return Address(uintptr_t(addr) & ~(alignment - 1));
|
||||
}
|
||||
|
||||
template <typename T> static inline T
|
||||
Min(const T &t1, const T &t2)
|
||||
{
|
||||
return t1 < t2 ? t1 : t2;
|
||||
}
|
||||
|
||||
template <typename T> static inline T
|
||||
Max(const T &t1, const T &t2)
|
||||
{
|
||||
return t1 > t2 ? t1 : t2;
|
||||
}
|
||||
|
||||
template <typename T> T
|
||||
ReturnAndVoid(T &t)
|
||||
{
|
||||
T saved = t;
|
||||
t = T();
|
||||
return saved;
|
||||
}
|
||||
|
||||
#define OFFSETOF(Class, Member) reinterpret_cast<size_t>(&((Class *)NULL)->Member)
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
# define KE_SIZET_FMT "%Iu"
|
||||
#elif defined(__GNUC__)
|
||||
# define KE_SIZET_FMT "%zu"
|
||||
#else
|
||||
# error "Implement format specifier string"
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__)
|
||||
# define KE_CRITICAL_LIKELY(x) __builtin_expect(!!(x), 1)
|
||||
#else
|
||||
# define KE_CRITICAL_LIKELY(x) x
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // _include_jitcraft_utility_h_
|
||||
@@ -0,0 +1,166 @@
|
||||
/* vim: set ts=2 sw=2 tw=99 et:
|
||||
*
|
||||
* Copyright (C) 2012 David Anderson
|
||||
*
|
||||
* This file is part of SourcePawn.
|
||||
*
|
||||
* SourcePawn is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* SourcePawn 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
|
||||
* SourcePawn. If not, see http://www.gnu.org/licenses/.
|
||||
*/
|
||||
#ifndef _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_
|
||||
#define _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_
|
||||
|
||||
#include <new>
|
||||
#include <stdlib.h>
|
||||
#include <ke_allocator_policies.h>
|
||||
#include <ke_utility.h>
|
||||
|
||||
namespace ke {
|
||||
|
||||
template <typename T, typename AllocPolicy = SystemAllocatorPolicy>
|
||||
class Vector : public AllocPolicy
|
||||
{
|
||||
public:
|
||||
Vector(AllocPolicy = AllocPolicy())
|
||||
: data(NULL),
|
||||
nitems(0),
|
||||
maxsize(0)
|
||||
{
|
||||
}
|
||||
|
||||
~Vector()
|
||||
{
|
||||
zap();
|
||||
}
|
||||
|
||||
void steal(Vector &other) {
|
||||
zap();
|
||||
data = other.data;
|
||||
nitems = other.nitems;
|
||||
maxsize = other.maxsize;
|
||||
other.reset();
|
||||
}
|
||||
|
||||
bool append(const T& item) {
|
||||
if (!growIfNeeded(1))
|
||||
return false;
|
||||
new (&data[nitems]) T(item);
|
||||
nitems++;
|
||||
return true;
|
||||
}
|
||||
void infallibleAppend(const T &item) {
|
||||
assert(growIfNeeded(1));
|
||||
new (&data[nitems]) T(item);
|
||||
nitems++;
|
||||
}
|
||||
T popCopy() {
|
||||
T t = at(length() - 1);
|
||||
pop();
|
||||
return t;
|
||||
}
|
||||
void pop() {
|
||||
assert(nitems);
|
||||
data[nitems - 1].~T();
|
||||
nitems--;
|
||||
}
|
||||
bool empty() const {
|
||||
return length() == 0;
|
||||
}
|
||||
size_t length() const {
|
||||
return nitems;
|
||||
}
|
||||
T& at(size_t i) {
|
||||
assert(i < length());
|
||||
return data[i];
|
||||
}
|
||||
const T& at(size_t i) const {
|
||||
assert(i < length());
|
||||
return data[i];
|
||||
}
|
||||
T& operator [](size_t i) {
|
||||
return at(i);
|
||||
}
|
||||
const T& operator [](size_t i) const {
|
||||
return at(i);
|
||||
}
|
||||
void clear() {
|
||||
nitems = 0;
|
||||
}
|
||||
const T &back() const {
|
||||
return at(length() - 1);
|
||||
}
|
||||
T &back() {
|
||||
return at(length() - 1);
|
||||
}
|
||||
|
||||
T *buffer() const {
|
||||
return data;
|
||||
}
|
||||
|
||||
bool ensure(size_t desired) {
|
||||
if (desired <= length())
|
||||
return true;
|
||||
|
||||
return growIfNeeded(desired - length());
|
||||
}
|
||||
|
||||
private:
|
||||
void zap() {
|
||||
for (size_t i = 0; i < nitems; i++)
|
||||
data[i].~T();
|
||||
this->free(data);
|
||||
}
|
||||
void reset() {
|
||||
data = NULL;
|
||||
nitems = 0;
|
||||
maxsize = 0;
|
||||
}
|
||||
|
||||
bool growIfNeeded(size_t needed)
|
||||
{
|
||||
if (!IsUintPtrAddSafe(nitems, needed)) {
|
||||
this->reportAllocationOverflow();
|
||||
return false;
|
||||
}
|
||||
if (nitems + needed < maxsize)
|
||||
return true;
|
||||
if (maxsize == 0)
|
||||
maxsize = 8;
|
||||
while (nitems + needed > maxsize) {
|
||||
if (!IsUintPtrMultiplySafe(maxsize, 2)) {
|
||||
this->reportAllocationOverflow();
|
||||
return false;
|
||||
}
|
||||
maxsize *= 2;
|
||||
}
|
||||
T* newdata = (T*)this->malloc(sizeof(T) * maxsize);
|
||||
if (newdata == NULL)
|
||||
return false;
|
||||
for (size_t i = 0; i < nitems; i++) {
|
||||
new (&newdata[i]) T(data[i]);
|
||||
data[i].~T();
|
||||
}
|
||||
this->free(data);
|
||||
data = newdata;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
T* data;
|
||||
size_t nitems;
|
||||
size_t maxsize;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* _INCLUDE_KEIMA_TPL_CPP_VECTOR_H_ */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* vim: set ts=4 :
|
||||
* vim: set ts=4 sw=4 tw=99 noet:
|
||||
* =============================================================================
|
||||
* SourcePawn
|
||||
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
|
||||
@@ -85,6 +85,8 @@ typedef uint32_t funcid_t; /**< Function index code */
|
||||
#define SP_ERROR_ABORTED 25 /**< Function call was aborted */
|
||||
#define SP_ERROR_CODE_TOO_OLD 26 /**< Code is too old for this VM */
|
||||
#define SP_ERROR_CODE_TOO_NEW 27 /**< Code is too new for this VM */
|
||||
#define SP_ERROR_OUT_OF_MEMORY 28 /**< Out of memory */
|
||||
#define SP_ERROR_INTEGER_OVERFLOW 29 /**< Integer overflow (-INT_MIN / -1) */
|
||||
//Hey you! Update the string table if you add to the end of me! */
|
||||
|
||||
/**********************************************
|
||||
|
||||
Reference in New Issue
Block a user