Refactor the JIT to use a newer, simpler macro assembler. (bug 5827, r=ann)

This commit is contained in:
David Anderson 2013-08-08 09:41:24 -07:00
parent ad543c909c
commit 9e56725406
26 changed files with 5150 additions and 7108 deletions

294
public/jit/assembler.h Normal file
View File

@ -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__

View File

@ -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 &dividend) {
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__

File diff suppressed because it is too large Load Diff

View File

@ -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_

View File

@ -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_

View File

@ -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_ */

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 : * vim: set ts=4 sw=4 tw=99 noet:
* ============================================================================= * =============================================================================
* SourcePawn * SourcePawn
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * 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_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_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_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! */ //Hey you! Update the string table if you add to the end of me! */
/********************************************** /**********************************************

View File

@ -22,8 +22,8 @@ binary.AddSourceFiles('sourcepawn/jit', [
'sp_vm_basecontext.cpp', 'sp_vm_basecontext.cpp',
'sp_vm_engine.cpp', 'sp_vm_engine.cpp',
'sp_vm_function.cpp', 'sp_vm_function.cpp',
'opcodes.cpp',
'x86/jit_x86.cpp', 'x86/jit_x86.cpp',
'x86/opcode_helpers.cpp',
'zlib/adler32.c', 'zlib/adler32.c',
'zlib/compress.c', 'zlib/compress.c',
'zlib/crc32.c', 'zlib/crc32.c',

View File

@ -1,3 +1,4 @@
// vim: set ts=8 sts=2 sw=2 tw=99 et:
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -12,573 +13,571 @@
using namespace SourcePawn; using namespace SourcePawn;
BaseRuntime::BaseRuntime() : m_Debug(&m_plugin), m_pPlugin(&m_plugin), m_pCtx(NULL), static inline bool
m_PubFuncs(NULL), m_PubJitFuncs(NULL), m_pCo(NULL), m_CompSerial(0) IsPointerCellAligned(void *p)
{ {
memset(&m_plugin, 0, sizeof(m_plugin)); return uintptr_t(p) % 4 == 0;
}
m_FuncCache = NULL; BaseRuntime::BaseRuntime()
m_MaxFuncs = 0; : m_Debug(&m_plugin),
m_NumFuncs = 0; m_pCtx(NULL),
m_PubFuncs(NULL),
m_PubJitFuncs(NULL),
co_(NULL),
m_CompSerial(0)
{
memset(&m_plugin, 0, sizeof(m_plugin));
memset(m_CodeHash, 0, sizeof(m_CodeHash)); m_MaxFuncs = 0;
memset(m_DataHash, 0, sizeof(m_DataHash)); m_NumFuncs = 0;
float_table_ = NULL;
function_map_ = NULL;
alt_pcode_ = NULL;
memset(m_CodeHash, 0, sizeof(m_CodeHash));
memset(m_DataHash, 0, sizeof(m_DataHash));
} }
BaseRuntime::~BaseRuntime() BaseRuntime::~BaseRuntime()
{ {
for (uint32_t i = 0; i < m_pPlugin->num_publics; i++) for (uint32_t i = 0; i < m_plugin.num_publics; i++)
{ delete m_PubFuncs[i];
delete m_PubFuncs[i]; delete [] m_PubFuncs;
m_PubFuncs[i] = NULL; delete [] m_PubJitFuncs;
} delete [] float_table_;
delete [] m_PubFuncs; delete [] function_map_;
delete [] m_PubJitFuncs; delete [] alt_pcode_;
for (unsigned int i = 0; i < m_NumFuncs; i++) for (size_t i = 0; i < m_JitFunctions.length(); i++)
{ delete m_JitFunctions[i];
delete m_FuncCache[i];
}
free(m_FuncCache);
delete m_pCtx; delete m_pCtx;
if (m_pCo != NULL) if (co_)
{ co_->Abort();
m_pCo->Abort();
}
free(m_pPlugin->base); free(m_plugin.base);
delete [] m_pPlugin->memory; delete [] m_plugin.memory;
delete [] m_pPlugin->publics; delete [] m_plugin.publics;
delete [] m_pPlugin->pubvars; delete [] m_plugin.pubvars;
delete [] m_pPlugin->natives; delete [] m_plugin.natives;
free(m_pPlugin->name); free(m_plugin.name);
}
void
BaseRuntime::SetupFloatNativeRemapping()
{
float_table_ = new floattbl_t[m_plugin.num_natives];
for (size_t i = 0; i < m_plugin.num_natives; i++) {
const char *name = m_plugin.natives[i].name;
if (!strcmp(name, "FloatAbs")) {
float_table_[i].found = true;
float_table_[i].index = OP_FABS;
} else if (!strcmp(name, "FloatAdd")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATADD;
} else if (!strcmp(name, "FloatSub")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATSUB;
} else if (!strcmp(name, "FloatMul")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATMUL;
} else if (!strcmp(name, "FloatDiv")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATDIV;
} else if (!strcmp(name, "float")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOAT;
} else if (!strcmp(name, "FloatCompare")) {
float_table_[i].found = true;
float_table_[i].index = OP_FLOATCMP;
} else if (!strcmp(name, "RoundToZero")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_ZERO;
} else if (!strcmp(name, "RoundToCeil")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_CEIL;
} else if (!strcmp(name, "RoundToFloor")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_FLOOR;
} else if (!strcmp(name, "RoundToNearest")) {
float_table_[i].found = true;
float_table_[i].index = OP_RND_TO_NEAREST;
}
}
}
unsigned
BaseRuntime::GetNativeReplacement(size_t index)
{
if (!float_table_[index].found)
return OP_NOP;
return float_table_[index].index;
}
void
BaseRuntime::SetName(const char *name)
{
m_plugin.name = strdup(name);
} }
static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params) static cell_t InvalidNative(IPluginContext *pCtx, const cell_t *params)
{ {
return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native"); return pCtx->ThrowNativeErrorEx(SP_ERROR_INVALID_NATIVE, "Invalid native");
} }
int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base) int BaseRuntime::CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base)
{ {
int set_err; char *nameptr;
char *nameptr; uint8_t sectnum = 0;
uint8_t sectnum = 0; sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
sp_plugin_t *plugin = m_pPlugin;
sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
memset(plugin, 0, sizeof(sp_plugin_t)); memset(&m_plugin, 0, sizeof(m_plugin));
plugin->base = base; m_plugin.base = base;
plugin->base_size = hdr->imagesize; m_plugin.base_size = hdr->imagesize;
set_err = SP_ERROR_NONE;
if (hdr->version == 0x0101) if (hdr->version == 0x0101)
{ m_plugin.debug.unpacked = true;
plugin->debug.unpacked = true;
}
/* We have to read the name section first */ /* We have to read the name section first */
for (sectnum = 0; sectnum < hdr->sections; sectnum++) for (sectnum = 0; sectnum < hdr->sections; sectnum++) {
{ nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs);
nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs); if (strcmp(nameptr, ".names") == 0) {
if (strcmp(nameptr, ".names") == 0) m_plugin.stringbase = (const char *)(base + secptr[sectnum].dataoffs);
{ break;
plugin->stringbase = (const char *)(base + secptr[sectnum].dataoffs); }
break; }
}
}
sectnum = 0; sectnum = 0;
/* Now read the rest of the sections */ /* Now read the rest of the sections */
while (sectnum < hdr->sections) while (sectnum < hdr->sections) {
{ nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
if (!(plugin->pcode) && !strcmp(nameptr, ".code")) if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) {
{ sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
if (cod->codeversion < SP_CODEVERS_JIT1) if (cod->codeversion < SP_CODEVERS_JIT1)
{ return SP_ERROR_CODE_TOO_OLD;
return SP_ERROR_CODE_TOO_OLD; if (cod->codeversion > SP_CODEVERS_JIT2)
} return SP_ERROR_CODE_TOO_NEW;
else if (cod->codeversion > SP_CODEVERS_JIT2)
{
return SP_ERROR_CODE_TOO_NEW;
}
plugin->pcode = base + secptr->dataoffs + cod->code; m_plugin.pcode = base + secptr->dataoffs + cod->code;
plugin->pcode_size = cod->codesize; m_plugin.pcode_size = cod->codesize;
plugin->flags = cod->flags; m_plugin.flags = cod->flags;
plugin->pcode_version = cod->codeversion; m_plugin.pcode_version = cod->codeversion;
} if (!IsPointerCellAligned(m_plugin.pcode)) {
else if (!(plugin->data) && !strcmp(nameptr, ".data")) // The JIT requires that pcode is cell-aligned, so if it's not, we
{ // remap the code segment to a new address.
sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs); alt_pcode_ = new uint8_t[m_plugin.pcode_size];
plugin->data = base + secptr->dataoffs + dat->data; memcpy(alt_pcode_, m_plugin.pcode, m_plugin.pcode_size);
plugin->data_size = dat->datasize; assert(IsPointerCellAligned(alt_pcode_));
plugin->mem_size = dat->memsize;
plugin->memory = new uint8_t[plugin->mem_size];
memcpy(plugin->memory, plugin->data, plugin->data_size);
}
else if ((plugin->publics == NULL) && !strcmp(nameptr, ".publics"))
{
sp_file_publics_t *publics;
publics = (sp_file_publics_t *)(base + secptr->dataoffs); m_plugin.pcode = alt_pcode_;
plugin->num_publics = secptr->size / sizeof(sp_file_publics_t); }
} else if (!(m_plugin.data) && !strcmp(nameptr, ".data")) {
sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
m_plugin.data = base + secptr->dataoffs + dat->data;
m_plugin.data_size = dat->datasize;
m_plugin.mem_size = dat->memsize;
m_plugin.memory = new uint8_t[m_plugin.mem_size];
memcpy(m_plugin.memory, m_plugin.data, m_plugin.data_size);
} else if ((m_plugin.publics == NULL) && !strcmp(nameptr, ".publics")) {
sp_file_publics_t *publics;
if (plugin->num_publics > 0) publics = (sp_file_publics_t *)(base + secptr->dataoffs);
{ m_plugin.num_publics = secptr->size / sizeof(sp_file_publics_t);
plugin->publics = new sp_public_t[plugin->num_publics];
for (uint32_t i = 0; i < plugin->num_publics; i++) if (m_plugin.num_publics > 0) {
{ m_plugin.publics = new sp_public_t[m_plugin.num_publics];
plugin->publics[i].code_offs = publics[i].address;
plugin->publics[i].funcid = (i << 1) | 1;
plugin->publics[i].name = plugin->stringbase + publics[i].name;
}
}
}
else if ((plugin->pubvars == NULL) && !strcmp(nameptr, ".pubvars"))
{
sp_file_pubvars_t *pubvars;
pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs); for (uint32_t i = 0; i < m_plugin.num_publics; i++) {
plugin->num_pubvars = secptr->size / sizeof(sp_file_pubvars_t); m_plugin.publics[i].code_offs = publics[i].address;
m_plugin.publics[i].funcid = (i << 1) | 1;
m_plugin.publics[i].name = m_plugin.stringbase + publics[i].name;
}
}
} else if ((m_plugin.pubvars == NULL) && !strcmp(nameptr, ".pubvars")) {
sp_file_pubvars_t *pubvars;
if (plugin->num_pubvars > 0) pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
{ m_plugin.num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
plugin->pubvars = new sp_pubvar_t[plugin->num_pubvars];
for (uint32_t i = 0; i < plugin->num_pubvars; i++) if (m_plugin.num_pubvars > 0) {
{ m_plugin.pubvars = new sp_pubvar_t[m_plugin.num_pubvars];
plugin->pubvars[i].name = plugin->stringbase + pubvars[i].name;
plugin->pubvars[i].offs = (cell_t *)(plugin->memory + pubvars[i].address);
}
}
}
else if ((plugin->natives == NULL) && !strcmp(nameptr, ".natives"))
{
sp_file_natives_t *natives;
natives = (sp_file_natives_t *)(base + secptr->dataoffs); for (uint32_t i = 0; i < m_plugin.num_pubvars; i++) {
plugin->num_natives = secptr->size / sizeof(sp_file_natives_t); m_plugin.pubvars[i].name = m_plugin.stringbase + pubvars[i].name;
m_plugin.pubvars[i].offs = (cell_t *)(m_plugin.memory + pubvars[i].address);
}
}
} else if ((m_plugin.natives == NULL) && !strcmp(nameptr, ".natives")) {
sp_file_natives_t *natives;
if (plugin->num_natives > 0) natives = (sp_file_natives_t *)(base + secptr->dataoffs);
{ m_plugin.num_natives = secptr->size / sizeof(sp_file_natives_t);
plugin->natives = new sp_native_t[plugin->num_natives];
for (uint32_t i = 0; i < plugin->num_natives; i++) if (m_plugin.num_natives > 0) {
{ m_plugin.natives = new sp_native_t[m_plugin.num_natives];
plugin->natives[i].flags = 0;
plugin->natives[i].pfn = InvalidNative;
plugin->natives[i].status = SP_NATIVE_UNBOUND;
plugin->natives[i].user = NULL;
plugin->natives[i].name = plugin->stringbase + natives[i].name;
}
}
}
else if (!(plugin->debug.files) && !strcmp(nameptr, ".dbg.files"))
{
plugin->debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
}
else if (!(plugin->debug.lines) && !strcmp(nameptr, ".dbg.lines"))
{
plugin->debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
}
else if (!(plugin->debug.symbols) && !strcmp(nameptr, ".dbg.symbols"))
{
plugin->debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
}
else if (!(plugin->debug.lines_num) && !strcmp(nameptr, ".dbg.info"))
{
sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
plugin->debug.files_num = inf->num_files;
plugin->debug.lines_num = inf->num_lines;
plugin->debug.syms_num = inf->num_syms;
}
else if (!(plugin->debug.stringbase) && !strcmp(nameptr, ".dbg.strings"))
{
plugin->debug.stringbase = (const char *)(base + secptr->dataoffs);
}
else if (strcmp(nameptr, ".dbg.natives") == 0)
{
plugin->debug.unpacked = false;
}
secptr++; for (uint32_t i = 0; i < m_plugin.num_natives; i++) {
sectnum++; m_plugin.natives[i].flags = 0;
} m_plugin.natives[i].pfn = InvalidNative;
m_plugin.natives[i].status = SP_NATIVE_UNBOUND;
m_plugin.natives[i].user = NULL;
m_plugin.natives[i].name = m_plugin.stringbase + natives[i].name;
}
}
} else if (!(m_plugin.debug.files) && !strcmp(nameptr, ".dbg.files")) {
m_plugin.debug.files = (sp_fdbg_file_t *)(base + secptr->dataoffs);
} else if (!(m_plugin.debug.lines) && !strcmp(nameptr, ".dbg.lines")) {
m_plugin.debug.lines = (sp_fdbg_line_t *)(base + secptr->dataoffs);
} else if (!(m_plugin.debug.symbols) && !strcmp(nameptr, ".dbg.symbols")) {
m_plugin.debug.symbols = (sp_fdbg_symbol_t *)(base + secptr->dataoffs);
} else if (!(m_plugin.debug.lines_num) && !strcmp(nameptr, ".dbg.info")) {
sp_fdbg_info_t *inf = (sp_fdbg_info_t *)(base + secptr->dataoffs);
m_plugin.debug.files_num = inf->num_files;
m_plugin.debug.lines_num = inf->num_lines;
m_plugin.debug.syms_num = inf->num_syms;
} else if (!(m_plugin.debug.stringbase) && !strcmp(nameptr, ".dbg.strings")) {
m_plugin.debug.stringbase = (const char *)(base + secptr->dataoffs);
} else if (strcmp(nameptr, ".dbg.natives") == 0) {
m_plugin.debug.unpacked = false;
}
if (plugin->pcode == NULL || plugin->data == NULL) secptr++;
{ sectnum++;
return SP_ERROR_FILE_FORMAT; }
}
if ((plugin->flags & SP_FLAG_DEBUG) && ( if (m_plugin.pcode == NULL || m_plugin.data == NULL)
!(plugin->debug.files) || return SP_ERROR_FILE_FORMAT;
!(plugin->debug.lines) ||
!(plugin->debug.symbols) ||
!(plugin->debug.stringbase) ))
{
return SP_ERROR_FILE_FORMAT;
}
if (m_pPlugin->num_publics > 0) if ((m_plugin.flags & SP_FLAG_DEBUG) && (
{ !(m_plugin.debug.files) ||
m_PubFuncs = new CFunction *[m_pPlugin->num_publics]; !(m_plugin.debug.lines) ||
memset(m_PubFuncs, 0, sizeof(CFunction *) * m_pPlugin->num_publics); !(m_plugin.debug.symbols) ||
m_PubJitFuncs = new JitFunction *[m_pPlugin->num_publics]; !(m_plugin.debug.stringbase) ))
memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_pPlugin->num_publics); {
} return SP_ERROR_FILE_FORMAT;
}
MD5 md5_pcode; if (m_plugin.num_publics > 0) {
md5_pcode.update(plugin->pcode, plugin->pcode_size); m_PubFuncs = new CFunction *[m_plugin.num_publics];
md5_pcode.finalize(); memset(m_PubFuncs, 0, sizeof(CFunction *) * m_plugin.num_publics);
md5_pcode.raw_digest(m_CodeHash); m_PubJitFuncs = new JitFunction *[m_plugin.num_publics];
memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_plugin.num_publics);
}
MD5 md5_data; MD5 md5_pcode;
md5_data.update(plugin->data, plugin->data_size); md5_pcode.update(m_plugin.pcode, m_plugin.pcode_size);
md5_data.finalize(); md5_pcode.finalize();
md5_data.raw_digest(m_DataHash); md5_pcode.raw_digest(m_CodeHash);
m_pPlugin->profiler = g_engine2.GetProfiler(); MD5 md5_data;
m_pCtx = new BaseContext(this); md5_data.update(m_plugin.data, m_plugin.data_size);
m_pCo = g_Jit.StartCompilation(this); md5_data.finalize();
md5_data.raw_digest(m_DataHash);
return SP_ERROR_NONE; m_plugin.profiler = g_engine2.GetProfiler();
m_pCtx = new BaseContext(this);
co_ = g_Jit.StartCompilation(this);
SetupFloatNativeRemapping();
function_map_size_ = m_plugin.pcode_size / sizeof(cell_t) + 1;
function_map_ = new JitFunction *[function_map_size_];
memset(function_map_, 0, function_map_size_ * sizeof(JitFunction *));
return SP_ERROR_NONE;
} }
int BaseRuntime::FindNativeByName(const char *name, uint32_t *index) void
BaseRuntime::AddJittedFunction(JitFunction *fn)
{ {
int high; m_JitFunctions.append(fn);
high = m_pPlugin->num_natives - 1; cell_t pcode_offset = fn->GetPCodeAddress();
assert(pcode_offset % 4 == 0);
for (uint32_t i=0; i<m_pPlugin->num_natives; i++) uint32_t pcode_index = pcode_offset / 4;
{ assert(pcode_index < function_map_size_);
if (strcmp(m_pPlugin->natives[i].name, name) == 0)
{
if (index)
{
*index = i;
}
return SP_ERROR_NONE;
}
}
return SP_ERROR_NOT_FOUND; function_map_[pcode_index] = fn;
} }
int BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native) JitFunction *
BaseRuntime::GetJittedFunctionByOffset(cell_t pcode_offset)
{ {
if (index >= m_pPlugin->num_natives) assert(pcode_offset % 4 == 0);
{
return SP_ERROR_INDEX;
}
if (native) uint32_t pcode_index = pcode_offset / 4;
{ assert(pcode_index < function_map_size_);
*native = &(m_pPlugin->natives[index]);
}
return SP_ERROR_NONE; return function_map_[pcode_index];
}
int
BaseRuntime::FindNativeByName(const char *name, uint32_t *index)
{
for (uint32_t i=0; i<m_plugin.num_natives; i++) {
if (strcmp(m_plugin.natives[i].name, name) == 0) {
if (index)
*index = i;
return SP_ERROR_NONE;
}
}
return SP_ERROR_NOT_FOUND;
}
int
BaseRuntime::GetNativeByIndex(uint32_t index, sp_native_t **native)
{
if (index >= m_plugin.num_natives)
return SP_ERROR_INDEX;
if (native)
*native = &(m_plugin.natives[index]);
return SP_ERROR_NONE;
} }
uint32_t BaseRuntime::GetNativesNum() uint32_t
BaseRuntime::GetNativesNum()
{ {
return m_pPlugin->num_natives; return m_plugin.num_natives;
} }
int BaseRuntime::FindPublicByName(const char *name, uint32_t *index) int
BaseRuntime::FindPublicByName(const char *name, uint32_t *index)
{ {
int diff, high, low; int diff, high, low;
uint32_t mid; uint32_t mid;
high = m_pPlugin->num_publics - 1; high = m_plugin.num_publics - 1;
low = 0; low = 0;
while (low <= high) while (low <= high) {
{ mid = (low + high) / 2;
mid = (low + high) / 2; diff = strcmp(m_plugin.publics[mid].name, name);
diff = strcmp(m_pPlugin->publics[mid].name, name); if (diff == 0) {
if (diff == 0) if (index)
{ *index = mid;
if (index) return SP_ERROR_NONE;
{ } else if (diff < 0) {
*index = mid; low = mid + 1;
} } else {
return SP_ERROR_NONE; high = mid - 1;
} else if (diff < 0) { }
low = mid + 1; }
} else {
high = mid - 1;
}
}
return SP_ERROR_NOT_FOUND; return SP_ERROR_NOT_FOUND;
} }
int BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic) int
BaseRuntime::GetPublicByIndex(uint32_t index, sp_public_t **pblic)
{ {
if (index >= m_pPlugin->num_publics) if (index >= m_plugin.num_publics)
{ return SP_ERROR_INDEX;
return SP_ERROR_INDEX;
}
if (pblic) if (pblic)
{ *pblic = &(m_plugin.publics[index]);
*pblic = &(m_pPlugin->publics[index]);
}
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
uint32_t BaseRuntime::GetPublicsNum() uint32_t
BaseRuntime::GetPublicsNum()
{ {
return m_pPlugin->num_publics; return m_plugin.num_publics;
} }
int BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar) int
BaseRuntime::GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar)
{ {
if (index >= m_pPlugin->num_pubvars) if (index >= m_plugin.num_pubvars)
{ return SP_ERROR_INDEX;
return SP_ERROR_INDEX;
}
if (pubvar) if (pubvar)
{ *pubvar = &(m_plugin.pubvars[index]);
*pubvar = &(m_pPlugin->pubvars[index]);
}
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
int BaseRuntime::FindPubvarByName(const char *name, uint32_t *index) int
BaseRuntime::FindPubvarByName(const char *name, uint32_t *index)
{ {
int diff, high, low; int diff, high, low;
uint32_t mid; uint32_t mid;
high = m_pPlugin->num_pubvars - 1; high = m_plugin.num_pubvars - 1;
low = 0; low = 0;
while (low <= high) while (low <= high) {
{ mid = (low + high) / 2;
mid = (low + high) / 2; diff = strcmp(m_plugin.pubvars[mid].name, name);
diff = strcmp(m_pPlugin->pubvars[mid].name, name); if (diff == 0) {
if (diff == 0) if (index)
{ *index = mid;
if (index) return SP_ERROR_NONE;
{ } else if (diff < 0) {
*index = mid; low = mid + 1;
} } else {
return SP_ERROR_NONE; high = mid - 1;
} }
else if (diff < 0) }
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
return SP_ERROR_NOT_FOUND; return SP_ERROR_NOT_FOUND;
} }
int BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr) int
BaseRuntime::GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr)
{ {
if (index >= m_pPlugin->num_pubvars) if (index >= m_plugin.num_pubvars)
{ return SP_ERROR_INDEX;
return SP_ERROR_INDEX;
}
*local_addr = (uint8_t *)m_pPlugin->pubvars[index].offs - m_pPlugin->memory; *local_addr = (uint8_t *)m_plugin.pubvars[index].offs - m_plugin.memory;
*phys_addr = m_pPlugin->pubvars[index].offs; *phys_addr = m_plugin.pubvars[index].offs;
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
uint32_t BaseRuntime::GetPubVarsNum() uint32_t
BaseRuntime::GetPubVarsNum()
{ {
return m_pPlugin->num_pubvars; return m_plugin.num_pubvars;
} }
IPluginContext *BaseRuntime::GetDefaultContext() IPluginContext *
BaseRuntime::GetDefaultContext()
{ {
return m_pCtx; return m_pCtx;
} }
IPluginDebugInfo *BaseRuntime::GetDebugInfo() IPluginDebugInfo *
BaseRuntime::GetDebugInfo()
{ {
return &m_Debug; return &m_Debug;
} }
IPluginFunction *BaseRuntime::GetFunctionById(funcid_t func_id) IPluginFunction *
BaseRuntime::GetFunctionById(funcid_t func_id)
{ {
CFunction *pFunc = NULL; CFunction *pFunc = NULL;
if (func_id & 1) if (func_id & 1) {
{ func_id >>= 1;
func_id >>= 1; if (func_id >= m_plugin.num_publics)
if (func_id >= m_pPlugin->num_publics) return NULL;
{ pFunc = m_PubFuncs[func_id];
return NULL; if (!pFunc) {
} m_PubFuncs[func_id] = new CFunction(this,
pFunc = m_PubFuncs[func_id]; (func_id << 1) | 1,
if (!pFunc) func_id);
{ pFunc = m_PubFuncs[func_id];
m_PubFuncs[func_id] = new CFunction(this, }
(func_id << 1) | 1, }
func_id);
pFunc = m_PubFuncs[func_id];
}
}
return pFunc; return pFunc;
} }
IPluginFunction *BaseRuntime::GetFunctionByName(const char *public_name) IPluginFunction *
BaseRuntime::GetFunctionByName(const char *public_name)
{ {
uint32_t index; uint32_t index;
if (FindPublicByName(public_name, &index) != SP_ERROR_NONE) if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
{ return NULL;
return NULL;
}
CFunction *pFunc = m_PubFuncs[index]; CFunction *pFunc = m_PubFuncs[index];
if (!pFunc) if (!pFunc) {
{ sp_public_t *pub = NULL;
sp_public_t *pub = NULL; GetPublicByIndex(index, &pub);
GetPublicByIndex(index, &pub); if (pub)
if (pub) m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
{ pFunc = m_PubFuncs[index];
m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index); }
}
pFunc = m_PubFuncs[index];
}
return pFunc; return pFunc;
} }
bool BaseRuntime::IsDebugging() bool BaseRuntime::IsDebugging()
{ {
return true; return true;
} }
void BaseRuntime::SetPauseState(bool paused) void BaseRuntime::SetPauseState(bool paused)
{ {
if (paused) if (paused)
{ {
m_pPlugin->run_flags |= SPFLAG_PLUGIN_PAUSED; m_plugin.run_flags |= SPFLAG_PLUGIN_PAUSED;
} }
else else
{ {
m_pPlugin->run_flags &= ~SPFLAG_PLUGIN_PAUSED; m_plugin.run_flags &= ~SPFLAG_PLUGIN_PAUSED;
} }
} }
bool BaseRuntime::IsPaused() bool BaseRuntime::IsPaused()
{ {
return ((m_pPlugin->run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED); return ((m_plugin.run_flags & SPFLAG_PLUGIN_PAUSED) == SPFLAG_PLUGIN_PAUSED);
} }
size_t BaseRuntime::GetMemUsage() size_t BaseRuntime::GetMemUsage()
{ {
size_t mem = 0; size_t mem = 0;
mem += sizeof(this); mem += sizeof(this);
mem += sizeof(sp_plugin_t); mem += sizeof(sp_plugin_t);
mem += sizeof(BaseContext); mem += sizeof(BaseContext);
mem += m_pPlugin->base_size; mem += m_plugin.base_size;
return mem; return mem;
} }
unsigned char *BaseRuntime::GetCodeHash() unsigned char *BaseRuntime::GetCodeHash()
{ {
return m_CodeHash; return m_CodeHash;
} }
unsigned char *BaseRuntime::GetDataHash() unsigned char *BaseRuntime::GetDataHash()
{ {
return m_DataHash; return m_DataHash;
} }
BaseContext *BaseRuntime::GetBaseContext() BaseContext *BaseRuntime::GetBaseContext()
{ {
return m_pCtx; return m_pCtx;
} }
int BaseRuntime::ApplyCompilationOptions(ICompilation *co) int
BaseRuntime::ApplyCompilationOptions(ICompilation *co)
{ {
if (co == NULL) if (co == NULL)
{ return SP_ERROR_NONE;
return SP_ERROR_NONE;
}
m_pCo = g_Jit.ApplyOptions(m_pCo, co); co_ = g_Jit.ApplyOptions(co_, co);
m_pPlugin->prof_flags = ((CompData *)m_pCo)->profile; m_plugin.prof_flags = ((CompData *)co_)->profile;
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
JitFunction *BaseRuntime::GetJittedFunction(uint32_t idx) int
BaseRuntime::CreateBlank(uint32_t heastk)
{ {
assert(idx <= m_NumFuncs); memset(&m_plugin, 0, sizeof(m_plugin));
if (idx == 0 || idx > m_NumFuncs) /* Align to cell_t bytes */
{ heastk += sizeof(cell_t);
return NULL; heastk -= heastk % sizeof(cell_t);
}
return m_FuncCache[idx - 1]; m_plugin.mem_size = heastk;
} m_plugin.memory = new uint8_t[heastk];
uint32_t BaseRuntime::AddJittedFunction(JitFunction *fn) m_plugin.profiler = g_engine2.GetProfiler();
{ m_pCtx = new BaseContext(this);
if (m_NumFuncs + 1 > m_MaxFuncs) co_ = g_Jit.StartCompilation(this);
{
if (m_MaxFuncs == 0) return SP_ERROR_NONE;
{
m_MaxFuncs = 8;
}
else
{
m_MaxFuncs *= 2;
}
m_FuncCache = (JitFunction **)realloc(
m_FuncCache,
sizeof(JitFunction *) * m_MaxFuncs);
}
m_FuncCache[m_NumFuncs++] = fn;
return m_NumFuncs;
}
int BaseRuntime::CreateBlank(uint32_t heastk)
{
memset(m_pPlugin, 0, sizeof(sp_plugin_t));
/* Align to cell_t bytes */
heastk += sizeof(cell_t);
heastk -= heastk % sizeof(cell_t);
m_pPlugin->mem_size = heastk;
m_pPlugin->memory = new uint8_t[heastk];
m_pPlugin->profiler = g_engine2.GetProfiler();
m_pCtx = new BaseContext(this);
m_pCo = g_Jit.StartCompilation(this);
return SP_ERROR_NONE;
} }

View File

@ -1,7 +1,9 @@
// vim: set ts=8 sw=2 sts=2 tw=99 et:
#ifndef _INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_ #ifndef _INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_
#define _INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_ #define _INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_
#include <sp_vm_api.h> #include <sp_vm_api.h>
#include <ke_vector.h>
#include "jit_shared.h" #include "jit_shared.h"
#include "sp_vm_function.h" #include "sp_vm_function.h"
@ -11,65 +13,93 @@ class JitFunction;
class DebugInfo : public IPluginDebugInfo class DebugInfo : public IPluginDebugInfo
{ {
public: public:
DebugInfo(sp_plugin_t *plugin); DebugInfo(sp_plugin_t *plugin);
public: public:
int LookupFile(ucell_t addr, const char **filename); int LookupFile(ucell_t addr, const char **filename);
int LookupFunction(ucell_t addr, const char **name); int LookupFunction(ucell_t addr, const char **name);
int LookupLine(ucell_t addr, uint32_t *line); int LookupLine(ucell_t addr, uint32_t *line);
private: private:
sp_plugin_t *m_pPlugin; sp_plugin_t *m_pPlugin;
};
struct floattbl_t
{
floattbl_t() {
found = false;
index = 0;
}
bool found;
unsigned int index;
}; };
/* Jit wants fast access to this so we expose things as public */ /* Jit wants fast access to this so we expose things as public */
class BaseRuntime : public SourcePawn::IPluginRuntime class BaseRuntime : public SourcePawn::IPluginRuntime
{ {
public: public:
BaseRuntime(); BaseRuntime();
~BaseRuntime(); ~BaseRuntime();
public:
virtual int CreateBlank(uint32_t heastk);
virtual int CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base);
virtual bool IsDebugging();
virtual IPluginDebugInfo *GetDebugInfo();
virtual int FindNativeByName(const char *name, uint32_t *index);
virtual int GetNativeByIndex(uint32_t index, sp_native_t **native);
virtual uint32_t GetNativesNum();
virtual int FindPublicByName(const char *name, uint32_t *index);
virtual int GetPublicByIndex(uint32_t index, sp_public_t **publicptr);
virtual uint32_t GetPublicsNum();
virtual int GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar);
virtual int FindPubvarByName(const char *name, uint32_t *index);
virtual int GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr);
virtual uint32_t GetPubVarsNum();
virtual IPluginFunction *GetFunctionByName(const char *public_name);
virtual IPluginFunction *GetFunctionById(funcid_t func_id);
virtual IPluginContext *GetDefaultContext();
virtual int ApplyCompilationOptions(ICompilation *co);
virtual void SetPauseState(bool paused);
virtual bool IsPaused();
virtual size_t GetMemUsage();
virtual unsigned char *GetCodeHash();
virtual unsigned char *GetDataHash();
JitFunction *GetJittedFunction(uint32_t idx);
uint32_t AddJittedFunction(JitFunction *fn);
public:
BaseContext *GetBaseContext();
private:
sp_plugin_t m_plugin;
unsigned int m_NumFuncs;
unsigned int m_MaxFuncs;
JitFunction **m_FuncCache;
public:
DebugInfo m_Debug;
sp_plugin_t *m_pPlugin;
BaseContext *m_pCtx;
CFunction **m_PubFuncs;
JitFunction **m_PubJitFuncs;
ICompilation *m_pCo;
unsigned int m_CompSerial;
unsigned char m_CodeHash[16]; public:
unsigned char m_DataHash[16]; virtual int CreateBlank(uint32_t heastk);
virtual int CreateFromMemory(sp_file_hdr_t *hdr, uint8_t *base);
virtual bool IsDebugging();
virtual IPluginDebugInfo *GetDebugInfo();
virtual int FindNativeByName(const char *name, uint32_t *index);
virtual int GetNativeByIndex(uint32_t index, sp_native_t **native);
virtual uint32_t GetNativesNum();
virtual int FindPublicByName(const char *name, uint32_t *index);
virtual int GetPublicByIndex(uint32_t index, sp_public_t **publicptr);
virtual uint32_t GetPublicsNum();
virtual int GetPubvarByIndex(uint32_t index, sp_pubvar_t **pubvar);
virtual int FindPubvarByName(const char *name, uint32_t *index);
virtual int GetPubvarAddrs(uint32_t index, cell_t *local_addr, cell_t **phys_addr);
virtual uint32_t GetPubVarsNum();
virtual IPluginFunction *GetFunctionByName(const char *public_name);
virtual IPluginFunction *GetFunctionById(funcid_t func_id);
virtual IPluginContext *GetDefaultContext();
virtual int ApplyCompilationOptions(ICompilation *co);
virtual void SetPauseState(bool paused);
virtual bool IsPaused();
virtual size_t GetMemUsage();
virtual unsigned char *GetCodeHash();
virtual unsigned char *GetDataHash();
JitFunction *GetJittedFunctionByOffset(cell_t pcode_offset);
void AddJittedFunction(JitFunction *fn);
void SetName(const char *name);
unsigned GetNativeReplacement(size_t index);
BaseContext *GetBaseContext();
const sp_plugin_t *plugin() const {
return &m_plugin;
}
private:
void SetupFloatNativeRemapping();
private:
sp_plugin_t m_plugin;
uint8_t *alt_pcode_;
unsigned int m_NumFuncs;
unsigned int m_MaxFuncs;
floattbl_t *float_table_;
JitFunction **function_map_;
size_t function_map_size_;
ke::Vector<JitFunction *> m_JitFunctions;
public:
DebugInfo m_Debug;
BaseContext *m_pCtx;
CFunction **m_PubFuncs;
JitFunction **m_PubJitFuncs;
private:
ICompilation *co_;
public:
unsigned int m_CompSerial;
unsigned char m_CodeHash[16];
unsigned char m_DataHash[16];
}; };
#endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_ #endif //_INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_

View File

@ -12,12 +12,12 @@ PROJECT = sourcepawn.jit.x86
OBJECTS = dll_exports.cpp \ OBJECTS = dll_exports.cpp \
x86/jit_x86.cpp \ x86/jit_x86.cpp \
x86/opcode_helpers.cpp \
sp_vm_basecontext.cpp \ sp_vm_basecontext.cpp \
sp_vm_engine.cpp \ sp_vm_engine.cpp \
sp_vm_function.cpp \ sp_vm_function.cpp \
engine2.cpp \ engine2.cpp \
BaseRuntime.cpp \ BaseRuntime.cpp \
opcodes.cpp \
jit_function.cpp \ jit_function.cpp \
md5/md5.cpp \ md5/md5.cpp \
zlib/adler32.c \ zlib/adler32.c \

View File

@ -0,0 +1,111 @@
# (C)2004-2008 SourceMod Development Team
# Makefile written by David "BAILOPAN" Anderson
SMSDK = ../..
MMSOURCE17 = ../../../mmsource-1.7
#####################################
### EDIT BELOW FOR OTHER PROJECTS ###
#####################################
PROJECT = spshell
OBJECTS = dll_exports.cpp \
x86/jit_x86.cpp \
sp_vm_basecontext.cpp \
sp_vm_engine.cpp \
sp_vm_function.cpp \
engine2.cpp \
BaseRuntime.cpp \
jit_function.cpp \
opcodes.cpp \
md5/md5.cpp \
zlib/adler32.c \
zlib/compress.c \
zlib/crc32.c \
zlib/deflate.c \
zlib/gzio.c \
zlib/infback.c \
zlib/inffast.c \
zlib/inflate.c \
zlib/inftrees.c \
zlib/trees.c \
zlib/uncompr.c \
zlib/zutil.c \
OBJECTS += ../../knight/shared/KeCodeAllocator.cpp
##############################################
### CONFIGURE ANY OTHER FLAGS/OPTIONS HERE ###
##############################################
C_OPT_FLAGS = -DNDEBUG -O3 -funroll-loops -pipe -fno-strict-aliasing
C_DEBUG_FLAGS = -D_DEBUG -DDEBUG -g -ggdb3
C_GCC4_FLAGS = -fvisibility=hidden
CXX_GCC4_FLAGS = -fvisibility-inlines-hidden
CXX = c++
CC = cc
LINK = -m32 -lm
INCLUDE = -I. -I.. -I$(SMSDK)/public -I$(SMSDK)/public/jit -I$(SMSDK)/public/jit/x86 \
-I$(SMSDK)/public/sourcepawn -I$(MMSOURCE17)/core/sourcehook -I$(SMSDK)/knight/shared -Ix86
CFLAGS += -D_LINUX -Dstricmp=strcasecmp -D_stricmp=strcasecmp -D_strnicmp=strncasecmp -Dstrnicmp=strncasecmp \
-D_snprintf=snprintf -D_vsnprintf=vsnprintf -D_alloca=alloca -Dstrcmpi=strcasecmp -Wall -DHAVE_STDINT_H \
-m32 -Wno-uninitialized -Werror -DSPSHELL -ggdb3 -Wno-unused
CXXFLAGS += -Wno-non-virtual-dtor -fno-exceptions -fno-rtti -Wno-delete-non-virtual-dtor
################################################
### DO NOT EDIT BELOW HERE FOR MOST PROJECTS ###
################################################
ifeq "$(DEBUG)" "true"
BIN_DIR = Debug.shell
CFLAGS += $(C_DEBUG_FLAGS)
else
BIN_DIR = Release.shell
CFLAGS += $(C_OPT_FLAGS)
endif
GCC_VERSION := $(shell $(CXX) -dumpversion >&1 | cut -b1)
ifeq "$(GCC_VERSION)" "4"
CFLAGS += $(C_GCC4_FLAGS)
CXXFLAGS += $(CXX_GCC4_FLAGS)
endif
OBJ_LINUX := $(OBJECTS:../../knight/shared/%.cpp=$(BIN_DIR)/knight/%.o)
OBJ_LINUX := $(OBJ_LINUX:%.cpp=$(BIN_DIR)/%.o)
OBJ_LINUX := $(OBJ_LINUX:%.c=$(BIN_DIR)/%.o)
default: all
$(BIN_DIR)/%.o: %.c
$(CC) $(INCLUDE) $(CFLAGS) -o $@ -c $<
$(BIN_DIR)/%.o: %.cpp
$(CXX) $(INCLUDE) $(CFLAGS) $(CXXFLAGS) -o $@ -c $<
$(BIN_DIR)/knight/%.o: ../../knight/shared/%.cpp
$(CXX) $(INCLUDE) $(CFLAGS) $(CXXFLAGS) -o $@ -c $<
all:
mkdir -p $(BIN_DIR)/x86
mkdir -p $(BIN_DIR)/md5
mkdir -p $(BIN_DIR)/zlib
mkdir -p $(BIN_DIR)/knight
$(MAKE) -f Makefile.shell jit
jit: $(OBJ_LINUX)
$(CXX) $(INCLUDE) $(OBJ_LINUX) $(LINK) -o $(BIN_DIR)/$(PROJECT)
debug:
$(MAKE) -f Makefile.shell all DEBUG=true
clean:
rm -f $(BIN_DIR)/x86/*.o
# rm -rf $(BIN_DIR)/zlib/*.o
# rm -rf $(BIN_DIR)/knight/*.o
rm -f $(BIN_DIR)/*.o
rm -f $(BIN_DIR)/$(PROJECT)

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 : * vim: set ts=4 sts=4 sw=4 tw=99 noet:
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -31,13 +31,199 @@
#include <sp_vm_api.h> #include <sp_vm_api.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h>
#include "x86/jit_x86.h" #include "x86/jit_x86.h"
#include "dll_exports.h" #include "dll_exports.h"
#include "sp_vm_engine.h" #include "sp_vm_engine.h"
#include "engine2.h" #include "engine2.h"
using namespace SourcePawn;
SourcePawnEngine2 g_engine2; SourcePawnEngine2 g_engine2;
#ifdef SPSHELL
template <typename T> class AutoT
{
public:
AutoT(T *t)
: t_(t)
{
}
~AutoT()
{
delete t_;
}
operator T *() const {
return t_;
}
bool operator !() const {
return !t_;
}
T * operator ->() const {
return t_;
}
private:
T *t_;
};
class ShellDebugListener : public IDebugListener
{
public:
void OnContextExecuteError(IPluginContext *ctx, IContextTrace *error) {
int n_err = error->GetErrorCode();
if (n_err != SP_ERROR_NATIVE)
{
fprintf(stderr, "plugin error: %s\n", error->GetErrorString());
}
if (const char *lastname = error->GetLastNative(NULL))
{
if (const char *custerr = error->GetCustomErrorString())
{
fprintf(stderr, "Native \"%s\" reported: %s", lastname, custerr);
} else {
fprintf(stderr, "Native \"%s\" encountered a generic error.", lastname);
}
}
if (!error->DebugInfoAvailable())
{
fprintf(stderr, "Debug info not available!\n");
return;
}
CallStackInfo stk_info;
int i = 0;
fprintf(stderr, "Displaying call stack trace:\n");
while (error->GetTraceInfo(&stk_info))
{
fprintf(stderr,
" [%d] Line %d, %s::%s()",
i++,
stk_info.line,
stk_info.filename,
stk_info.function);
}
}
void OnDebugSpew(const char *msg, ...) {
#if !defined(NDEBUG) && defined(DEBUG)
va_list ap;
va_start(ap, msg);
vfprintf(stderr, msg, ap);
va_end(ap);
#endif
}
};
static cell_t Print(IPluginContext *cx, const cell_t *params)
{
char *p;
cx->LocalToString(params[1], &p);
return printf("%s", p);
}
static cell_t PrintNum(IPluginContext *cx, const cell_t *params)
{
return printf("%d\n", params[1]);
}
static cell_t PrintNums(IPluginContext *cx, const cell_t *params)
{
for (size_t i = 1; i <= size_t(params[0]); i++) {
int err;
cell_t *addr;
if ((err = cx->LocalToPhysAddr(params[i], &addr)) != SP_ERROR_NONE)
return cx->ThrowNativeErrorEx(err, "Could not read argument");
fprintf(stdout, "%d", *addr);
if (i != size_t(params[0]))
fprintf(stdout, ", ");
}
fprintf(stdout, "\n");
return 1;
}
static void BindNative(IPluginRuntime *rt, const char *name, SPVM_NATIVE_FUNC fn)
{
int err;
uint32_t index;
if ((err = rt->FindNativeByName(name, &index)) != SP_ERROR_NONE)
return;
sp_native_t *native;
if (rt->GetNativeByIndex(index, &native) != SP_ERROR_NONE)
return;
native->pfn = fn;
native->status = SP_NATIVE_BOUND;
}
static cell_t PrintFloat(IPluginContext *cx, const cell_t *params)
{
return printf("%f\n", sp_ctof(params[1]));
}
static int Execute(const char *file)
{
ICompilation *co = g_engine2.StartCompilation();
if (!co) {
fprintf(stderr, "Could not create a compilation context\n");
return 1;
}
int err;
AutoT<IPluginRuntime> rt(g_engine2.LoadPlugin(co, file, &err));
if (!rt) {
fprintf(stderr, "Could not load plugin: %s\n", g_engine1.GetErrorString(err));
return 1;
}
BindNative(rt, "print", Print);
BindNative(rt, "printnum", PrintNum);
BindNative(rt, "printnums", PrintNums);
BindNative(rt, "printfloat", PrintFloat);
IPluginFunction *fun = rt->GetFunctionByName("main");
if (!fun)
return 0;
IPluginContext *cx = rt->GetDefaultContext();
int result = fun->Execute2(cx, &err);
if (err != SP_ERROR_NONE) {
fprintf(stderr, "Error executing main(): %s\n", g_engine1.GetErrorString(err));
return 1;
}
return result;
}
int main(int argc, char **argv)
{
if (argc != 2) {
fprintf(stderr, "Usage: <file>\n");
return 1;
}
if (!g_engine2.Initialize()) {
fprintf(stderr, "Could not initialize ISourcePawnEngine2\n");
return 1;
}
ShellDebugListener debug;
g_engine1.SetDebugListener(&debug);
int errcode = Execute(argv[1]);
g_engine1.SetDebugListener(NULL);
g_engine2.Shutdown();
return errcode;
}
#else
EXPORTFUNC ISourcePawnEngine *GetSourcePawnEngine1() EXPORTFUNC ISourcePawnEngine *GetSourcePawnEngine1()
{ {
return &g_engine1; return &g_engine1;
@ -47,6 +233,7 @@ EXPORTFUNC ISourcePawnEngine2 *GetSourcePawnEngine2()
{ {
return &g_engine2; return &g_engine2;
} }
#endif
#if defined __linux__ || defined __APPLE__ #if defined __linux__ || defined __APPLE__
extern "C" void __cxa_pure_virtual(void) extern "C" void __cxa_pure_virtual(void)

View File

@ -106,19 +106,19 @@ IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file
#endif #endif
) )
{ {
pRuntime->m_pPlugin->name = strdup(&file[i+1]); pRuntime->SetName(&file[i+1]);
break; break;
} }
} }
if (pRuntime->m_pPlugin->name == NULL) if (!pRuntime->plugin()->name)
{ {
pRuntime->m_pPlugin->name = strdup(file); pRuntime->SetName(file);
} }
pRuntime->ApplyCompilationOptions(co); pRuntime->ApplyCompilationOptions(co);
fclose(fp); fclose(fp);
return pRuntime; return pRuntime;
@ -204,7 +204,7 @@ IPluginRuntime *SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t
return NULL; return NULL;
} }
rt->m_pPlugin->name = strdup(name != NULL ? name : "<anonymous>"); rt->SetName(name != NULL ? name : "<anonymous>");
rt->ApplyCompilationOptions(NULL); rt->ApplyCompilationOptions(NULL);

137
sourcepawn/jit/opcodes.cpp Normal file
View File

@ -0,0 +1,137 @@
/**
* vim: set ts=8 sw=2 tw=99 sts=2 et:
* =============================================================================
* SourceMod
* 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$
*/
#include "opcodes.h"
#include "jit_shared.h"
using namespace SourcePawn;
const char *OpcodeNames[] = {
#define _(op, text) text,
OPCODE_LIST(_)
#undef _
NULL
};
void
SourcePawn::SpewOpcode(const sp_plugin_t *plugin, cell_t *start, cell_t *cip)
{
fprintf(stdout, " [%05d:%04d]", cip - (cell_t *)plugin->pcode, cip - start);
if (*cip >= OPCODES_TOTAL) {
fprintf(stdout, " unknown-opcode\n");
return;
}
OPCODE op = (OPCODE)*cip;
fprintf(stdout, " %s ", OpcodeNames[op]);
switch (op) {
case OP_PUSH_C:
case OP_PUSH_ADR:
case OP_SHL_C_PRI:
case OP_SHL_C_ALT:
case OP_SHR_C_PRI:
case OP_SHR_C_ALT:
case OP_ADD_C:
case OP_SMUL_C:
case OP_EQ_C_PRI:
case OP_EQ_C_ALT:
case OP_TRACKER_PUSH_C:
case OP_STACK:
case OP_PUSH_S:
case OP_HEAP:
case OP_GENARRAY:
case OP_GENARRAY_Z:
fprintf(stdout, "%d", cip[1]);
break;
case OP_JUMP:
case OP_JZER:
case OP_JNZ:
case OP_JEQ:
case OP_JNEQ:
case OP_JSLESS:
case OP_JSGRTR:
case OP_JSGEQ:
case OP_JSLEQ:
fprintf(stdout, "%05d:%04d",
cip[1] / 4,
((cell_t *)plugin->pcode + cip[1] / 4) - start);
break;
case OP_SYSREQ_C:
case OP_SYSREQ_N:
{
uint32_t index = cip[1];
if (index < plugin->num_natives)
fprintf(stdout, "%s", plugin->natives[index].name);
if (op == OP_SYSREQ_N)
fprintf(stdout, " ; (%d args, index %d)", cip[2], index);
else
fprintf(stdout, " ; (index %d)", index);
break;
}
case OP_PUSH2_C:
case OP_PUSH2:
case OP_PUSH2_S:
case OP_PUSH2_ADR:
fprintf(stdout, "%d, %d", cip[1], cip[2]);
break;
case OP_PUSH3_C:
case OP_PUSH3:
case OP_PUSH3_S:
case OP_PUSH3_ADR:
fprintf(stdout, "%d, %d, %d", cip[1], cip[2], cip[3]);
break;
case OP_PUSH4_C:
case OP_PUSH4:
case OP_PUSH4_S:
case OP_PUSH4_ADR:
fprintf(stdout, "%d, %d, %d, %d", cip[1], cip[2], cip[3], cip[4]);
break;
case OP_PUSH5_C:
case OP_PUSH5:
case OP_PUSH5_S:
case OP_PUSH5_ADR:
fprintf(stdout, "%d, %d, %d, %d, %d", cip[1], cip[2], cip[3], cip[4], cip[5]);
break;
default:
break;
}
fprintf(stdout, "\n");
}

262
sourcepawn/jit/opcodes.h Normal file
View File

@ -0,0 +1,262 @@
/**
* vim: set ts=8 sw=2 tw=99 sts=2 et:
* =============================================================================
* SourceMod
* 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_JIT_X86_OPCODES_H_
#define _INCLUDE_SOURCEPAWN_JIT_X86_OPCODES_H_
#include <sp_vm_api.h>
// Opcodes labelled "UNGEN" cannot be generated by the compiler. Quite a few,
// if they could, make little sense in the context of a JIT and could not
// work anyway. Opcodes technically present in sc4.c/sc7.c (respectively,
// the code emitter and peephole optimizer) are not necessarily ever generated.
// For example, lref.pri and lref.alt would be used if a reference could be
// stored in the global scope; but they can't, so they are unreachable.
//
// Most opcodes have been manually verified. A few have not, as they are only
// produced by the peephole optimizer, or not produced at all, or eliminated
// during optimization. We generate them anyway, just in case, but they have
// not been tested.
// lref.s.alt (phopt only)
// stor.alt (never)
// stor.s.alt (never)
// sref.s.alt (never)
// lidx.b (phopt only, probably impossible)
// idxaddr.b (phopt only, looks difficult)
// move.pri (eliminated in phopt)
// shl.c.pri (eliminated in phopt)
// shl.c.alt (eliminated in phopt)
// shr.c.pri (eliminated in phopt)
// shr.c.alt (eliminated in phopt)
// eq.c.alt (never)
// inc.alt (never)
// dec.alt (never)
// sdiv (never)
// nop (never in function bodies)
//
// Additionally, some opcodes which were supported in the earlier JIT are no
// longer supported because of the above:
// lref.pri/alt
// sref.pri/alt
// sign.pri/alt
#define OPCODE_LIST(_) \
_(NONE, "none") \
_(LOAD_PRI, "load.pri") \
_(LOAD_ALT, "load.alt") \
_(LOAD_S_PRI, "load.s.pri") \
_(LOAD_S_ALT, "load.s.alt") \
_(UNGEN_LREF_PRI, "lref.pri") \
_(UNGEN_LREF_ALT, "lref.alt") \
_(LREF_S_PRI, "lref.s.pri") \
_(LREF_S_ALT, "lref.s.alt") \
_(LOAD_I, "load.i") \
_(LODB_I, "lodb.i") \
_(CONST_PRI, "const.pri") \
_(CONST_ALT, "const.alt") \
_(ADDR_PRI, "addr.pri") \
_(ADDR_ALT, "addr.alt") \
_(STOR_PRI, "stor.pri") \
_(STOR_ALT, "stor.alt") \
_(STOR_S_PRI, "stor.s.pri") \
_(STOR_S_ALT, "stor.s.alt") \
_(UNGEN_SREF_PRI, "sref.pri") \
_(UNGEN_SREF_ALT, "sref.alt") \
_(SREF_S_PRI, "sref.s.pri") \
_(SREF_S_ALT, "sref.s.alt") \
_(STOR_I, "stor.i") \
_(STRB_I, "strb.i") \
_(LIDX, "lidx") \
_(LIDX_B, "lidx.b") \
_(IDXADDR, "idxaddr") \
_(IDXADDR_B, "idxaddr.b") \
_(UNGEN_ALIGN_PRI,"align.pri") \
_(UNGEN_ALIGN_ALT,"align.alt") \
_(UNGEN_LCTRL, "lctrl") \
_(UNGEN_SCTRL, "sctrl") \
_(MOVE_PRI, "move.pri") \
_(MOVE_ALT, "move.alt") \
_(XCHG, "xchg") \
_(PUSH_PRI, "push.pri") \
_(PUSH_ALT, "push.alt") \
_(UNGEN_PUSH_R, "push.r") \
_(PUSH_C, "push.c") \
_(PUSH, "push") \
_(PUSH_S, "push.s") \
_(POP_PRI, "pop.pri") \
_(POP_ALT, "pop.alt") \
_(STACK, "stack") \
_(HEAP, "heap") \
_(PROC, "proc") \
_(UNGEN_RET, "ret") \
_(RETN, "retn") \
_(CALL, "call") \
_(UNGEN_CALL_PRI, "call.pri") \
_(JUMP, "jump") \
_(UNGEN_JREL, "jrel") \
_(JZER, "jzer") \
_(JNZ, "jnz") \
_(JEQ, "jeq") \
_(JNEQ, "jneq") \
_(UNGEN_JLESS, "jsless") \
_(UNGEN_JLEQ, "jleq") \
_(UNGEN_JGRTR, "jgrtr") \
_(UNGEN_JGEQ, "jgeq") \
_(JSLESS, "jsless") \
_(JSLEQ, "jsleq") \
_(JSGRTR, "jsgrtr") \
_(JSGEQ, "jsgeq") \
_(SHL, "shl") \
_(SHR, "shr") \
_(SSHR, "sshr") \
_(SHL_C_PRI, "shl.c.pri") \
_(SHL_C_ALT, "shl.c.alt") \
_(SHR_C_PRI, "shr.c.pri") \
_(SHR_C_ALT, "shr.c.alt") \
_(SMUL, "smul") \
_(SDIV, "sdiv") \
_(SDIV_ALT, "sdiv.alt") \
_(UNGEN_UMUL, "umul") \
_(UNGEN_UDIV, "udiv") \
_(UNGEN_UDIV_ALT, "udiv.alt") \
_(ADD, "add") \
_(SUB, "sub") \
_(SUB_ALT, "sub.alt") \
_(AND, "and") \
_(OR, "or") \
_(XOR, "xor") \
_(NOT, "not") \
_(NEG, "neg") \
_(INVERT, "invert") \
_(ADD_C, "add.c") \
_(SMUL_C, "smul.c") \
_(ZERO_PRI, "zero.pri") \
_(ZERO_ALT, "zero.alt") \
_(ZERO, "zero") \
_(ZERO_S, "zero.s") \
_(UNGEN_SIGN_PRI, "sign.pri") \
_(UNGEN_SIGN_ALT, "sign.alt") \
_(EQ, "eq") \
_(NEQ, "neq") \
_(UNGEN_LESS, "less") \
_(UNGEN_LEQ, "leq") \
_(UNGEN_GRTR, "grtr") \
_(UNGEN_GEQ, "geq") \
_(SLESS, "sless") \
_(SLEQ, "sleq") \
_(SGRTR, "sgrtr") \
_(SGEQ, "sgeq") \
_(EQ_C_PRI, "eq.c.pri") \
_(EQ_C_ALT, "eq.c.alt") \
_(INC_PRI, "inc.pri") \
_(INC_ALT, "inc.alt") \
_(INC, "inc") \
_(INC_S, "inc.s") \
_(INC_I, "inc.i") \
_(DEC_PRI, "dec.pri") \
_(DEC_ALT, "dec.alt") \
_(DEC, "dec") \
_(DEC_S, "dec.s") \
_(DEC_I, "dec.i") \
_(MOVS, "movs") \
_(UNGEN_CMPS, "cmps") \
_(FILL, "fill") \
_(HALT, "halt") \
_(BOUNDS, "bounds") \
_(UNGEN_SYSREQ_PRI,"sysreq.pri") \
_(SYSREQ_C, "sysreq.c") \
_(UNGEN_FILE, "file") \
_(UNGEN_LINE, "line") \
_(UNGEN_SYMBOL, "symbol") \
_(UNGEN_SRANGE, "srange") \
_(UNGEN_JUMP_PRI, "jump.pri") \
_(SWITCH, "switch") \
_(CASETBL, "casetbl") \
_(SWAP_PRI, "swap.pri") \
_(SWAP_ALT, "swap.alt") \
_(PUSH_ADR, "push.adr") \
_(NOP, "nop") \
_(SYSREQ_N, "sysreq.n") \
_(UNGEN_SYMTAG, "symtag") \
_(BREAK, "break") \
_(PUSH2_C, "push2.c") \
_(PUSH2, "push2") \
_(PUSH2_S, "push2.s") \
_(PUSH2_ADR, "push2.adr") \
_(PUSH3_C, "push3.c") \
_(PUSH3, "push3") \
_(PUSH3_S, "push3.s") \
_(PUSH3_ADR, "push3.adr") \
_(PUSH4_C, "push4.c") \
_(PUSH4, "push4") \
_(PUSH4_S, "push4.s") \
_(PUSH4_ADR, "push4.adr") \
_(PUSH5_C, "push5.c") \
_(PUSH5, "push5") \
_(PUSH5_S, "push5.s") \
_(PUSH5_ADR, "push5.adr") \
_(LOAD_BOTH, "load.both") \
_(LOAD_S_BOTH, "load.s.both") \
_(CONST, "const") \
_(CONST_S, "const.s") \
_(UNGEN_SYSREQ_D, "sysreq.d") \
_(UNGEB_SYSREQ_ND,"sysreq.nd") \
_(TRACKER_PUSH_C, "trk.push.c") \
_(TRACKER_POP_SETHEAP,"trk.pop") \
_(GENARRAY, "genarray") \
_(GENARRAY_Z, "genarray.z") \
_(STRADJUST_PRI, "stradjust.pri") \
_(UNGEN_STKADJUST,"stackadjust") \
_(ENDPROC, "endproc") \
_(FABS, "fabs") \
_(FLOAT, "float") \
_(FLOATADD, "floatadd") \
_(FLOATSUB, "floatsub") \
_(FLOATMUL, "floatmul") \
_(FLOATDIV, "floatdiv") \
_(RND_TO_NEAREST, "round") \
_(RND_TO_FLOOR, "floor") \
_(RND_TO_CEIL, "ceil") \
_(RND_TO_ZERO, "rndtozero") \
_(FLOATCMP, "floatcmp")
enum OPCODE {
#define _(op, text) OP_##op,
OPCODE_LIST(_)
#undef _
OPCODES_TOTAL
};
namespace SourcePawn {
void SpewOpcode(const sp_plugin_t *plugin, cell_t *start, cell_t *cip);
}
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODES_H_

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 : * vim: set ts=4 sw=4 tw=99 noet:
* ============================================================================= * =============================================================================
* SourcePawn * SourcePawn
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -46,7 +46,6 @@ using namespace SourcePawn;
BaseContext::BaseContext(BaseRuntime *pRuntime) BaseContext::BaseContext(BaseRuntime *pRuntime)
{ {
m_pRuntime = pRuntime; m_pRuntime = pRuntime;
m_pPlugin = m_pRuntime->m_pPlugin;
m_InExec = false; m_InExec = false;
m_CustomMsg = false; m_CustomMsg = false;
@ -75,8 +74,8 @@ BaseContext::BaseContext(BaseRuntime *pRuntime)
m_pNullString = NULL; m_pNullString = NULL;
} }
m_ctx.hp = m_pPlugin->data_size; m_ctx.hp = m_pRuntime->plugin()->data_size;
m_ctx.sp = m_pPlugin->mem_size - sizeof(cell_t); m_ctx.sp = m_pRuntime->plugin()->mem_size - sizeof(cell_t);
m_ctx.frm = m_ctx.sp; m_ctx.frm = m_ctx.sp;
m_ctx.n_err = SP_ERROR_NONE; m_ctx.n_err = SP_ERROR_NONE;
m_ctx.n_idx = SP_ERROR_NONE; m_ctx.n_idx = SP_ERROR_NONE;
@ -204,7 +203,7 @@ int BaseContext::HeapAlloc(unsigned int cells, cell_t *local_addr, cell_t **phys
return SP_ERROR_HEAPLOW; return SP_ERROR_HEAPLOW;
} }
addr = (cell_t *)(m_pPlugin->memory + m_ctx.hp); addr = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.hp);
/* store size of allocation in cells */ /* store size of allocation in cells */
*addr = (cell_t)cells; *addr = (cell_t)cells;
addr++; addr++;
@ -229,12 +228,12 @@ int BaseContext::HeapPop(cell_t local_addr)
/* check the bounds of this address */ /* check the bounds of this address */
local_addr -= sizeof(cell_t); local_addr -= sizeof(cell_t);
if (local_addr < (cell_t)m_pPlugin->data_size || local_addr >= m_ctx.sp) if (local_addr < (cell_t)m_pRuntime->plugin()->data_size || local_addr >= m_ctx.sp)
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
addr = (cell_t *)(m_pPlugin->memory + local_addr); addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
cellcount = (*addr) * sizeof(cell_t); cellcount = (*addr) * sizeof(cell_t);
/* check if this memory count looks valid */ /* check if this memory count looks valid */
if ((signed)(m_ctx.hp - cellcount - sizeof(cell_t)) != local_addr) if ((signed)(m_ctx.hp - cellcount - sizeof(cell_t)) != local_addr)
@ -250,7 +249,7 @@ int BaseContext::HeapPop(cell_t local_addr)
int BaseContext::HeapRelease(cell_t local_addr) int BaseContext::HeapRelease(cell_t local_addr)
{ {
if (local_addr < (cell_t)m_pPlugin->data_size) if (local_addr < (cell_t)m_pRuntime->plugin()->data_size)
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
@ -334,14 +333,14 @@ int BaseContext::BindNativeToAny(SPVM_NATIVE_FUNC native)
int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr) int BaseContext::LocalToPhysAddr(cell_t local_addr, cell_t **phys_addr)
{ {
if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
if (phys_addr) if (phys_addr)
{ {
*phys_addr = (cell_t *)(m_pPlugin->memory + local_addr); *phys_addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
} }
return SP_ERROR_NONE; return SP_ERROR_NONE;
@ -365,11 +364,11 @@ int BaseContext::PushCellArray(cell_t *local_addr, cell_t **phys_addr, cell_t ar
int BaseContext::LocalToString(cell_t local_addr, char **addr) int BaseContext::LocalToString(cell_t local_addr, char **addr)
{ {
if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
*addr = (char *)(m_pPlugin->memory + local_addr); *addr = (char *)(m_pRuntime->plugin()->memory + local_addr);
return SP_ERROR_NONE; return SP_ERROR_NONE;
} }
@ -385,7 +384,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
size_t len; size_t len;
if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
|| (local_addr < 0) || ((ucell_t)local_addr >= m_pPlugin->mem_size)) || (local_addr < 0) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
@ -396,7 +395,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
} }
len = strlen(source); len = strlen(source);
dest = (char *)(m_pPlugin->memory + local_addr); dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
if (len >= bytes) if (len >= bytes)
{ {
@ -455,7 +454,7 @@ int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const cha
if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp)) if (((local_addr >= m_ctx.hp) && (local_addr < m_ctx.sp))
|| (local_addr < 0) || (local_addr < 0)
|| ((ucell_t)local_addr >= m_pPlugin->mem_size)) || ((ucell_t)local_addr >= m_pRuntime->plugin()->mem_size))
{ {
return SP_ERROR_INVALID_ADDRESS; return SP_ERROR_INVALID_ADDRESS;
} }
@ -466,7 +465,7 @@ int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const cha
} }
len = strlen(source); len = strlen(source);
dest = (char *)(m_pPlugin->memory + local_addr); dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
if ((size_t)len >= maxbytes) if ((size_t)len >= maxbytes)
{ {
@ -586,21 +585,18 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
/* We got this far. It's time to start profiling. */ /* We got this far. It's time to start profiling. */
if ((m_pPlugin->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS) if ((m_pRuntime->plugin()->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
{ {
serial = m_pPlugin->profiler->OnCallbackBegin(this, pubfunc); serial = m_pRuntime->plugin()->profiler->OnCallbackBegin(this, pubfunc);
} }
/* See if we have to compile the callee. */ /* See if we have to compile the callee. */
if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL) if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
{ {
uint32_t func_idx;
/* We might not have to - check pcode offset. */ /* We might not have to - check pcode offset. */
if ((func_idx = FuncLookup((CompData *)m_pRuntime->m_pCo, pubfunc->code_offs)) != 0) fn = m_pRuntime->GetJittedFunctionByOffset(pubfunc->code_offs);
if (fn)
{ {
fn = m_pRuntime->GetJittedFunction(func_idx);
assert(fn != NULL);
m_pRuntime->m_PubJitFuncs[public_id] = fn; m_pRuntime->m_PubJitFuncs[public_id] = fn;
} }
else else
@ -629,7 +625,7 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
/* Push parameters */ /* Push parameters */
m_ctx.sp -= sizeof(cell_t) * (num_params + 1); m_ctx.sp -= sizeof(cell_t) * (num_params + 1);
sp = (cell_t *)(m_pPlugin->memory + m_ctx.sp); sp = (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.sp);
sp[0] = num_params; sp[0] = num_params;
for (unsigned int i = 0; i < num_params; i++) for (unsigned int i = 0; i < num_params; i++)
@ -687,9 +683,9 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
m_ctx.hp = save_hp; m_ctx.hp = save_hp;
m_ctx.rp = save_rp; m_ctx.rp = save_rp;
if ((m_pPlugin->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS) if ((m_pRuntime->plugin()->prof_flags & SP_PROF_CALLBACKS) == SP_PROF_CALLBACKS)
{ {
m_pPlugin->profiler->OnCallbackEnd(serial); m_pRuntime->plugin()->profiler->OnCallbackEnd(serial);
} }
m_ctx.err_cip = save_cip; m_ctx.err_cip = save_cip;
@ -746,7 +742,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
{ {
uint32_t max, iter; uint32_t max, iter;
sp_fdbg_symbol_t *sym; sp_fdbg_symbol_t *sym;
sp_fdbg_arraydim_t *arr;
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols); uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
max = m_pPlugin->debug.syms_num; max = m_pPlugin->debug.syms_num;
@ -765,7 +760,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
if (sym->dimcount > 0) if (sym->dimcount > 0)
{ {
cursor += sizeof(sp_fdbg_symbol_t); cursor += sizeof(sp_fdbg_symbol_t);
arr = (sp_fdbg_arraydim_t *)cursor;
cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount; cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
continue; continue;
} }
@ -779,7 +773,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
{ {
uint32_t max, iter; uint32_t max, iter;
sp_u_fdbg_symbol_t *sym; sp_u_fdbg_symbol_t *sym;
sp_u_fdbg_arraydim_t *arr;
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols); uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
max = m_pPlugin->debug.syms_num; max = m_pPlugin->debug.syms_num;
@ -798,7 +791,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
if (sym->dimcount > 0) if (sym->dimcount > 0)
{ {
cursor += sizeof(sp_u_fdbg_symbol_t); cursor += sizeof(sp_u_fdbg_symbol_t);
arr = (sp_u_fdbg_arraydim_t *)cursor;
cursor += sizeof(sp_u_fdbg_arraydim_t) * sym->dimcount; cursor += sizeof(sp_u_fdbg_arraydim_t) * sym->dimcount;
continue; continue;
} }
@ -848,7 +840,7 @@ int BaseContext::GetLastNativeError()
cell_t *BaseContext::GetLocalParams() cell_t *BaseContext::GetLocalParams()
{ {
return (cell_t *)(m_pPlugin->memory + m_ctx.frm + (2 * sizeof(cell_t))); return (cell_t *)(m_pRuntime->plugin()->memory + m_ctx.frm + (2 * sizeof(cell_t)));
} }
void BaseContext::SetKey(int k, void *value) void BaseContext::SetKey(int k, void *value)

View File

@ -100,7 +100,6 @@ private:
void SetErrorMessage(const char *msg, va_list ap); void SetErrorMessage(const char *msg, va_list ap);
void _SetErrorMessage(const char *msg, ...); void _SetErrorMessage(const char *msg, ...);
private: private:
sp_plugin_t *m_pPlugin;
cell_t *m_pNullVec; cell_t *m_pNullVec;
cell_t *m_pNullString; cell_t *m_pNullString;
char m_MsgCache[1024]; char m_MsgCache[1024];

View File

@ -58,7 +58,7 @@ SourcePawnEngine g_engine1;
using namespace SourcePawn; using namespace SourcePawn;
#define ERROR_MESSAGE_MAX 25 #define ERROR_MESSAGE_MAX 29
static const char *g_ErrorMsgTable[] = static const char *g_ErrorMsgTable[] =
{ {
NULL, NULL,
@ -89,6 +89,8 @@ static const char *g_ErrorMsgTable[] =
"Call was aborted", "Call was aborted",
"Plugin format is too old", "Plugin format is too old",
"Plugin format is too new", "Plugin format is too new",
"Out of memory",
"Integer overflow"
}; };
const char *SourcePawnEngine::GetErrorString(int error) const char *SourcePawnEngine::GetErrorString(int error)

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/** /**
* vim: set ts=4 : * vim: set ts=4 sw=4 tw=99 et:
* ============================================================================= * =============================================================================
* SourceMod * SourceMod
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -35,164 +35,180 @@
#include <sp_vm_types.h> #include <sp_vm_types.h>
#include <sp_vm_api.h> #include <sp_vm_api.h>
#include <KeCodeAllocator.h> #include <KeCodeAllocator.h>
#include "jit_helpers.h" #include <assembler-x86.h>
#include <ke_vector.h>
#include "jit_shared.h" #include "jit_shared.h"
#include "BaseRuntime.h" #include "BaseRuntime.h"
#include "jit_function.h" #include "jit_function.h"
#include "opcodes.h"
using namespace SourcePawn; using namespace SourcePawn;
#define JIT_INLINE_ERRORCHECKS (1<<0) #define JIT_INLINE_ERRORCHECKS (1<<0)
#define JIT_INLINE_NATIVES (1<<1) #define JIT_INLINE_NATIVES (1<<1)
#define STACK_MARGIN 64 //8 parameters of safety, I guess #define STACK_MARGIN 64 //8 parameters of safety, I guess
#define JIT_FUNCMAGIC 0x214D4148 //magic function offset #define JIT_FUNCMAGIC 0x214D4148 //magic function offset
#define JITVARS_TRACKER 0 //important: don't change this to avoid trouble #define JITVARS_TRACKER 0 //important: don't change this to avoid trouble
#define JITVARS_BASECTX 1 //important: don't change this aWOAWOGJQG I LIKE HAM #define JITVARS_BASECTX 1 //important: don't change this aWOAWOGJQG I LIKE HAM
#define JITVARS_PROFILER 2 //profiler #define JITVARS_PROFILER 2 //profiler
#define JITVARS_PLUGIN 3 //sp_plugin_t #define JITVARS_PLUGIN 3 //sp_plugin_t
#define sDIMEN_MAX 5 //this must mirror what the compiler has. #define sDIMEN_MAX 5 //this must mirror what the compiler has.
#define GET_CONTEXT(c) ((IPluginContext *)c->vm[JITVARS_BASECTX]) #define GET_CONTEXT(c) ((IPluginContext *)c->vm[JITVARS_BASECTX])
typedef struct tracker_s typedef struct tracker_s
{ {
size_t size; size_t size;
ucell_t *pBase; ucell_t *pBase;
ucell_t *pCur; ucell_t *pCur;
} tracker_t; } tracker_t;
typedef struct funcinfo_s typedef struct funcinfo_s
{ {
unsigned int magic; unsigned int magic;
unsigned int index; unsigned int index;
} funcinfo_t; } funcinfo_t;
typedef struct functracker_s typedef struct functracker_s
{ {
unsigned int num_functions; unsigned int num_functions;
unsigned int code_size; unsigned int code_size;
} functracker_t; } functracker_t;
struct floattbl_t struct CallThunk
{ {
floattbl_t() Label call;
{ cell_t pcode_offset;
found = false;
index = 0;
}
bool found;
unsigned int index;
};
struct call_thunk_t CallThunk(cell_t pcode_offset)
{ : pcode_offset(pcode_offset)
jitoffs_t patch_addr; {
cell_t pcode_offs; }
jitoffs_t thunk_addr;
}; };
class CompData : public ICompilation class CompData : public ICompilation
{ {
public: public:
CompData() CompData()
: runtime(NULL), : profile(0),
plugin(NULL), inline_level(0)
rebase(NULL), {
jit_float_table(NULL), };
profile(0), bool SetOption(const char *key, const char *val);
inline_level(0), void Abort();
error_set(SP_ERROR_NONE),
num_thunks(0),
max_thunks(0),
thunks(NULL)
{
};
bool SetOption(const char *key, const char *val);
void SetRuntime(BaseRuntime *runtime);
void Abort();
public: public:
BaseRuntime *runtime; /* runtime handle */ cell_t cur_func; /* current func pcode offset */
sp_plugin_t *plugin; /* plugin handle */ /* Options */
uint8_t *rebase; /* relocation map */ int profile; /* profiling flags */
floattbl_t *jit_float_table; int inline_level; /* inline optimization level */
cell_t cur_func; /* current func pcode offset */ /* Per-compilation properties */
/* Options */ unsigned int func_idx; /* current function index */
int profile; /* profiling flags */ };
int inline_level; /* inline optimization level */
/* Per-compilation properties */ class Compiler
int error_set; /* error code to halt process */ {
unsigned int func_idx; /* current function index */ public:
jitoffs_t jit_error_bounds; Compiler(BaseRuntime *rt, cell_t pcode_offs);
jitoffs_t jit_error_divzero; ~Compiler();
jitoffs_t jit_error_stacklow;
jitoffs_t jit_error_stackmin; JitFunction *emit(int *errp);
jitoffs_t jit_error_memaccess;
jitoffs_t jit_error_heaplow; private:
jitoffs_t jit_error_heapmin; bool setup(cell_t pcode_offs);
jitoffs_t jit_extern_error; /* returning generic error */ bool emitOp(OPCODE op);
jitoffs_t jit_sysreq_c; /* old version! */ cell_t readCell();
uint32_t num_thunks; /* number of thunks needed */
uint32_t max_thunks; /* maximum number of thunks */ private:
call_thunk_t *thunks; /* thunk array */ Label *labelAt(size_t offset);
bool emitCall();
bool emitNativeCall(OPCODE op);
bool emitSwitch();
void emitGenArray(bool autozero);
void emitCallThunks();
void emitCheckAddress(Register reg);
void emitErrorPath(Label *dest, int code);
void emitErrorPaths();
private:
AssemblerX86 masm;
BaseRuntime *rt_;
const sp_plugin_t *plugin_;
int error_;
uint32_t pcode_start_;
cell_t *code_start_;
cell_t *cip_;
cell_t *code_end_;
Label *jump_map_;
// Errors
Label error_bounds_;
Label error_heap_low_;
Label error_heap_min_;
Label error_stack_low_;
Label error_stack_min_;
Label error_divide_by_zero_;
Label error_memaccess_;
Label error_integer_overflow_;
Label extern_error_;
ke::Vector<CallThunk *> thunks_; //:TODO: free
}; };
class JITX86 class JITX86
{ {
public: public:
JITX86(); JITX86();
public:
bool InitializeJIT(); public:
void ShutdownJIT(); bool InitializeJIT();
ICompilation *StartCompilation(BaseRuntime *runtime); void ShutdownJIT();
ICompilation *StartCompilation(); ICompilation *StartCompilation(BaseRuntime *runtime);
void SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx); ICompilation *StartCompilation();
void FreeContextVars(sp_context_t *ctx); void SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx);
SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData); void FreeContextVars(sp_context_t *ctx);
void DestroyFakeNative(SPVM_NATIVE_FUNC func); SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData);
JitFunction *CompileFunction(BaseRuntime *runtime, cell_t pcode_offs, int *err); void DestroyFakeNative(SPVM_NATIVE_FUNC func);
ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT); JitFunction *CompileFunction(BaseRuntime *runtime, cell_t pcode_offs, int *err);
int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result); ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT);
public: int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result);
void *GetGenArrayIntrinsic();
void *GetReturnPoint(); public:
void *GetRoundingTable(); ExternalAddress GetGenArrayIntrinsic() {
void *AllocCode(size_t size); return ExternalAddress(m_pJitGenArray);
void FreeCode(void *code); }
private: ExternalAddress GetUniversalReturn() {
void *m_pJitEntry; /* Entry function */ return ExternalAddress(m_pJitReturn);
void *m_pJitReturn; /* Return point for errors */ }
int m_RoundTable[3]; /* [-1, 0, 1] rounding table */ void *AllocCode(size_t size);
void *m_pJitGenArray; /* Generates an array */ void FreeCode(void *code);
private:
void *m_pJitEntry; /* Entry function */
void *m_pJitReturn; /* Universal return address */
void *m_pJitGenArray; /* Generates an array */
}; };
cell_t NativeCallback(sp_context_t *ctx, ucell_t native_idx, cell_t *params); const Register pri = eax;
cell_t NativeCallback_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params); const Register alt = edx;
uint32_t FuncLookup(CompData *data, cell_t pcode_offs); const Register stk = edi;
jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative=false); const Register dat = ebp;
void *CompileThunk(BaseRuntime *runtime, cell_t pcode_ffs, void *jmploc_addr); const Register tmp = ecx;
const Register info = esi;
const Register frm = ebx;
#define AMX_REG_PRI REG_EAX #define AMX_NUM_INFO_VARS 9
#define AMX_REG_ALT REG_EDX
#define AMX_REG_STK REG_EDI
#define AMX_REG_DAT REG_EBP
#define AMX_REG_TMP REG_ECX
#define AMX_REG_INFO REG_ESI
#define AMX_REG_FRM REG_EBX
#define AMX_NUM_INFO_VARS 9 #define AMX_INFO_FRAME 0 //(same thing as above)
#define AMX_INFO_HEAP 4 //not relocated
#define AMX_INFO_FRM AMX_REG_INFO //not relocated #define AMX_INFO_RETVAL 8 //physical
#define AMX_INFO_FRAME 0 //(same thing as above) #define AMX_INFO_CONTEXT 12 //physical
#define AMX_INFO_HEAP 4 //not relocated #define AMX_INFO_STACKTOP 16 //relocated
#define AMX_INFO_RETVAL 8 //physical #define AMX_INFO_CIP 20 //pcode CIP
#define AMX_INFO_CONTEXT 12 //physical #define AMX_INFO_DATASIZE 24 //plugin->data_size
#define AMX_INFO_STACKTOP 16 //relocated #define AMX_INFO_MEMORY 28 //plugin->memory
#define AMX_INFO_CIP 20 //pcode CIP #define AMX_INFO_NSTACK 32 //native stack
#define AMX_INFO_DATASIZE 24 //plugin->data_size
#define AMX_INFO_MEMORY 28 //plugin->memory
#define AMX_INFO_NSTACK 32 //native stack
extern Knight::KeCodeCache *g_pCodeCache; extern Knight::KeCodeCache *g_pCodeCache;
extern JITX86 g_Jit; extern JITX86 g_Jit;

View File

@ -1,706 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* 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$
*/
#include <limits.h>
#include <string.h>
#include <stdlib.h>
#include "jit_x86.h"
#include "opcode_helpers.h"
#include "x86_macros.h"
jitoffs_t Write_Execute_Function(JitWriter *jit)
{
/**
* The variables we're passed in:
* void *vars[], void *entry_func
*/
/**
* !NOTE!
* Currently, we do not accept ctx->frm as the new frame pointer.
* Instead, we copy the frame from the stack pointer.
* This is because we do not support resuming or sleeping!
*/
//push ebp
//mov ebp, esp
IA32_Push_Reg(jit, REG_EBP);
IA32_Mov_Reg_Rm(jit, REG_EBP, REG_ESP, MOD_REG);
//push esi
//push edi
//push ebx
IA32_Push_Reg(jit, REG_ESI);
IA32_Push_Reg(jit, REG_EDI);
IA32_Push_Reg(jit, REG_EBX);
/* Prep us for doing the real work */
//mov esi, [ebp+param0] ;get vars
//mov ecx, [ebp+param1] ;get entry addr
//mov eax, [esi+MEMORY] ;get memory base
//mov edx, [esi+CONTEXT] ;get context
IA32_Mov_Reg_Rm_Disp8(jit, REG_ESI, REG_EBP, 8 + 4*0);
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_EBP, 8 + 4*1);
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_ESI, AMX_INFO_MEMORY);
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_ESI, AMX_INFO_CONTEXT);
/* Set up run-time registers */
//mov edi, [edx+SP] ;non-reloc SP
//add edi, eax ;reloc SP
//mov ebp, eax ;DAT
//mov ebx, edi ;reloc FRM
//mov [esi+NSTACK], esp ;save ESP
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDI, REG_EDX, offsetof(sp_context_t, sp));
IA32_Add_Rm_Reg(jit, REG_EDI, REG_EAX, MOD_REG);
IA32_Mov_Reg_Rm(jit, REG_EBP, REG_EAX, MOD_REG);
IA32_Mov_Reg_Rm(jit, REG_EBX, REG_EDI, MOD_REG);
IA32_Mov_Rm_Reg_Disp8(jit, REG_ESI, REG_ESP, AMX_INFO_NSTACK);
/* by now, everything is set up, so we can call into the plugin */
//call ecx
IA32_Call_Reg(jit, REG_ECX);
/* if the code flow gets to here, there was a normal return */
//mov ecx, [esi+RETVAL] ;get retval pointer
//mov [ecx], eax ;store retval from PRI
//mov eax, SP_ERROR_NONE ;set no error
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, AMX_REG_INFO, AMX_INFO_RETVAL);
IA32_Mov_Rm_Reg(jit, REG_ECX, AMX_REG_PRI, MOD_MEM_REG);
IA32_Mov_Reg_Imm32(jit, REG_EAX, SP_ERROR_NONE);
/* save where error checking/halting functions should go to */
jitoffs_t offs_return = jit->get_outputpos();
//mov esp, [esi+NSTACK] ;restore stack pointer
IA32_Mov_Reg_Rm_Disp8(jit, REG_ESP, REG_ESI, AMX_INFO_NSTACK);
/* Restore SP */
//mov ecx, [esi+CONTEXT]
//sub edi, ebp
//mov [ecx+SP], edi
IA32_Mov_Reg_Rm_Disp8(jit, REG_ECX, REG_ESI, AMX_INFO_CONTEXT);
IA32_Sub_Reg_Rm(jit, REG_EDI, REG_EBP, MOD_REG);
IA32_Mov_Rm_Reg_Disp8(jit, REG_ECX, REG_EDI, offsetof(sp_context_t, sp));
//pop ebx
//pop edi
//pop esi
//pop ebp
//ret
IA32_Pop_Reg(jit, REG_EBX);
IA32_Pop_Reg(jit, REG_EDI);
IA32_Pop_Reg(jit, REG_ESI);
IA32_Pop_Reg(jit, REG_EBP);
IA32_Return(jit);
return offs_return;
}
void Write_GetError(JitWriter *jit)
{
//mov eax, [esi+info.context]
//mov eax, [eax+ctx.error]
//jmp [jit_return]
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EAX, offsetof(sp_context_t, n_err));
IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint());
}
void Write_SetError(JitWriter *jit, int error)
{
//mov eax, <error>
//jmp [jit_return]
IA32_Mov_Reg_Imm32(jit, REG_EAX, error);
IA32_Jump_Imm32_Abs(jit, g_Jit.GetReturnPoint());
}
void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg)
{
//test reg, reg
//jz :error
IA32_Test_Rm_Reg(jit, reg, reg, MOD_REG);
IA32_Jump_Cond_Imm32_Rel(jit, CC_Z, ((CompData *)jit->data)->jit_error_divzero);
}
void Write_CheckHeap_Min(JitWriter *jit)
{
/* Check if the stack went beyond the heap low.
* This usually means there was a compiler error.
* NOTE: Special optimization here.
* The heap low is always known ahead of time! :)
*/
CompData *data = (CompData *)jit->data;
//cmp [esi+info.heap], <heaplow>
//jb :error
IA32_Cmp_Rm_Imm32_Disp8(jit, AMX_REG_INFO, AMX_INFO_HEAP, data->plugin->data_size);
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, data->jit_error_heapmin);
}
void Write_CheckHeap_Low(JitWriter *jit)
{
/* Check if the heap is trying to grow beyond the stack.
*/
//mov ecx, [esi+info.heap]
//lea ecx, [ebp+ecx+STACK_MARGIN]
//cmp ecx, edi
//ja :error ; I think this is right
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
IA32_Jump_Cond_Imm32_Rel(jit, CC_A, ((CompData *)jit->data)->jit_error_heaplow);
}
void Write_CheckStack_Min(JitWriter *jit)
{
/* Check if the stack went beyond the stack top
* This usually means there was a compiler error.
*/
//cmp edi, [esi+info.stacktop]
//jae :error
IA32_Cmp_Reg_Rm_Disp8(jit, AMX_REG_STK, AMX_REG_INFO, AMX_INFO_STACKTOP);
IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_stackmin);
}
void Write_CheckStack_Low(JitWriter *jit)
{
/* Check if the stack went beyond the heap boundary.
* Unfortunately this one isn't as quick as the other check.
* The stack margin check is important for sysreq.n having space.
*/
//mov ecx, [esi+info.heap]
//lea ecx, [ebp+ecx+STACK_MARGIN]
//cmp edi, ecx
//jb :error ; I think this is right
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, AMX_REG_TMP, NOSCALE, STACK_MARGIN);
IA32_Cmp_Reg_Rm(jit, AMX_REG_STK, AMX_REG_TMP, MOD_REG);
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_stacklow);
}
void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg)
{
/* :TODO: Should this be checking for below heaplow?
* The old JIT did not.
*/
/**
* :TODO: If we can't find a nicer way of doing this,
* then scrap it on high optimizations. The second portion is not needed at all!
*/
/* Part 1: Check if we're in the memory bounds */
//cmp <reg>, <stpu>
//jae :error
IA32_Cmp_Rm_Imm32(jit, MOD_REG, reg, ((CompData *)jit->data)->plugin->mem_size);
IA32_Jump_Cond_Imm32_Rel(jit, CC_AE, ((CompData *)jit->data)->jit_error_memaccess);
/* Part 2: Check if we're in the invalid region between HP and SP */
jitoffs_t jmp;
//cmp <reg>, [esi+info.heap]
//jb :continue
//lea ecx, [ebp+<reg>]
//cmp edi, ecx
//jb :error
//:continue
IA32_Cmp_Reg_Rm_Disp8(jit, reg, AMX_REG_INFO, AMX_INFO_HEAP);
jmp = IA32_Jump_Cond_Imm8(jit, CC_B, 0);
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_TMP, AMX_REG_DAT, reg, NOSCALE, 0);
IA32_Cmp_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_STK, MOD_REG);
IA32_Jump_Cond_Imm32_Rel(jit, CC_B, ((CompData *)jit->data)->jit_error_memaccess);
IA32_Send_Jump8_Here(jit, jmp);
}
void Macro_PushN_Addr(JitWriter *jit, int i)
{
//push eax
//mov eax, [esi+frm]
//loop i times:
// lea ecx, [eax+<val>]
// mov [edi-4*i], ecx
//sub edi, 4*N
//pop eax
cell_t val;
int n = 1;
IA32_Push_Reg(jit, AMX_REG_PRI);
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
do
{
val = jit->read_cell();
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
IA32_Lea_DispRegImm8(jit, AMX_REG_TMP, AMX_REG_PRI, (jit_int8_t)val);
else
IA32_Lea_DispRegImm32(jit, AMX_REG_TMP, AMX_REG_PRI, val);
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
} while (n++ < i);
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
IA32_Pop_Reg(jit, AMX_REG_PRI);
}
void Macro_PushN_S(JitWriter *jit, int i)
{
//loop i times:
// mov ecx, [ebx+<val>]
// mov [edi-4*i], ecx
//sub edi, 4*N
cell_t val;
int n = 1;
do
{
val = jit->read_cell();
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_FRM, (jit_int8_t)val);
else
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_FRM, val);
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
} while (n++ < i);
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
}
void Macro_PushN_C(JitWriter *jit, int i)
{
//loop i times:
// mov [edi-4*i], <val>
//sub edi, 4*N
int n = 1;
do
{
IA32_Mov_Rm_Imm32_Disp8(jit, AMX_REG_STK, jit->read_cell(), -4*n);
} while (n++ < i);
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
}
void Macro_PushN(JitWriter *jit, int i)
{
//loop i times:
// mov ecx, [ebp+<val>]
// mov [edi-4*i], ecx
//sub edi, 4*N
cell_t val;
int n = 1;
do
{
val = jit->read_cell();
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_DAT, (jit_int8_t)val);
else
IA32_Mov_Reg_Rm_Disp32(jit, AMX_REG_TMP, AMX_REG_DAT, val);
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_STK, AMX_REG_TMP, -4*n);
} while (n++ < i);
IA32_Sub_Rm_Imm8(jit, AMX_REG_STK, 4*i, MOD_REG);
}
void WriteOp_Sysreq_C_Function(JitWriter *jit)
{
/* The small daddy of the big daddy of opcodes.
* ecx - native index
*/
CompData *data = (CompData *)jit->data;
/* save registers we will need */
//push edx
IA32_Push_Reg(jit, AMX_REG_ALT);
/* Align the stack to 16 bytes */
//push ebx
//mov ebx, esp
//and esp, 0xFFFFFF0
//sub esp, 4
IA32_Push_Reg(jit, REG_EBX);
IA32_Mov_Reg_Rm(jit, REG_EBX, REG_ESP, MOD_REG);
IA32_And_Rm_Imm8(jit, REG_ESP, MOD_REG, -16);
IA32_Sub_Rm_Imm8(jit, REG_ESP, 4, MOD_REG);
/* push some callback stuff */
//push edi ; stack
//push ecx ; native index
IA32_Push_Reg(jit, AMX_REG_STK);
IA32_Push_Reg(jit, REG_ECX);
/* Relocate stack, heap, frm information, then store back */
//mov eax, [esi+context]
//mov ecx, [esi+hea]
//sub edi, ebp
//mov [eax+hp], ecx
//mov ecx, [esi]
//mov [eax+sp], edi
//mov [eax+frm], ecx
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_HEAP);
IA32_Sub_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, hp));
IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_INFO_FRM, MOD_MEM_REG);
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_STK, offsetof(sp_context_t, sp));
IA32_Mov_Rm_Reg_Disp8(jit, REG_EAX, AMX_REG_TMP, offsetof(sp_context_t, frm));
/* finally, push the last parameter and make the call */
//push eax ; context
//call NativeCallback
IA32_Push_Reg(jit, REG_EAX);
jitoffs_t call = IA32_Call_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, (void *)NativeCallback);
/* Test for error */
//mov ecx, [esi+context]
//cmp [ecx+err], 0
//jnz :error
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Cmp_Rm_Disp8_Imm8(jit, AMX_REG_TMP, offsetof(sp_context_t, n_err), 0);
IA32_Jump_Cond_Imm32_Rel(jit, CC_NZ, data->jit_extern_error);
/* restore what we damaged */
//mov esp, ebx
//pop ebx
//add edi, ebp
//pop edx
IA32_Mov_Reg_Rm(jit, REG_ESP, REG_EBX, MOD_REG);
IA32_Pop_Reg(jit, REG_EBX);
IA32_Add_Reg_Rm(jit, AMX_REG_STK, AMX_REG_DAT, MOD_REG);
IA32_Pop_Reg(jit, AMX_REG_ALT);
//ret
IA32_Return(jit);
}
typedef struct array_creation_s
{
const cell_t *dim_list; /* Dimension sizes */
cell_t dim_count; /* Number of dimensions */
cell_t *data_offs; /* Current offset AFTER the indirection vectors (data) */
cell_t *base; /* array base */
} array_creation_t;
cell_t _GenerateArrayIndirectionVectors(array_creation_t *ar, int dim, cell_t cur_offs)
{
cell_t write_offs = cur_offs;
cell_t *data_offs = ar->data_offs;
cur_offs += ar->dim_list[dim];
/**
* Dimension n-x where x > 2 will have sub-vectors.
* Otherwise, we just need to reference the data section.
*/
if (ar->dim_count > 2 && dim < ar->dim_count - 2)
{
/**
* For each index at this dimension, write offstes to our sub-vectors.
* After we write one sub-vector, we generate its sub-vectors recursively.
* At the end, we're given the next offset we can use.
*/
for (int i = 0; i < ar->dim_list[dim]; i++)
{
ar->base[write_offs] = (cur_offs - write_offs) * sizeof(cell_t);
write_offs++;
cur_offs = _GenerateArrayIndirectionVectors(ar, dim + 1, cur_offs);
}
} else {
/**
* In this section, there are no sub-vectors, we need to write offsets
* to the data. This is separate so the data stays in one big chunk.
* The data offset will increment by the size of the last dimension,
* because that is where the data is finally computed as.
*/
for (int i = 0; i < ar->dim_list[dim]; i++)
{
ar->base[write_offs] = (*data_offs - write_offs) * sizeof(cell_t);
write_offs++;
*data_offs = *data_offs + ar->dim_list[dim + 1];
}
}
return cur_offs;
}
static cell_t calc_indirection(const array_creation_t *ar, cell_t dim)
{
cell_t size = ar->dim_list[dim];
if (dim < ar->dim_count - 2)
{
size += ar->dim_list[dim] * calc_indirection(ar, dim + 1);
}
return size;
}
void GenerateArrayIndirectionVectors(cell_t *arraybase, cell_t dims[], cell_t _dimcount, bool autozero)
{
array_creation_t ar;
cell_t data_offs;
/* Reverse the dimensions */
cell_t dim_list[sDIMEN_MAX];
int cur_dim = 0;
for (int i = _dimcount - 1; i >= 0; i--)
{
dim_list[cur_dim++] = dims[i];
}
ar.base = arraybase;
ar.dim_list = dim_list;
ar.dim_count = _dimcount;
ar.data_offs = &data_offs;
data_offs = calc_indirection(&ar, 0);
_GenerateArrayIndirectionVectors(&ar, 0, 0);
}
/**
* A few notes about this function.
* I was more concerned about efficient use of registers here, rather than
* fine-tuned optimization. The reason is that the code is already complicated,
* and it is very easy to mess up.
*/
void WriteIntrinsic_GenArray(JitWriter *jit)
{
jitoffs_t err1, err2;
/**
* save important values
*/
//push ebx
//push eax
//push edx
//push ecx ;value is referenced on stack
IA32_Push_Reg(jit, REG_EBX);
IA32_Push_Reg(jit, REG_EAX);
IA32_Push_Reg(jit, REG_EDX);
IA32_Push_Reg(jit, REG_ECX);
/**
* Calculate how many cells will be needed.
*/
//mov edx, [edi] ;get last dimension's count
//mov eax, 1 ;position at second to last dimension
//:loop
//cmp eax, [esp] ;compare to # of params
//jae :done ;end loop if done
//mov ecx, [edi+eax*4] ;get dimension size
//imul edx, ecx ;multiply by size
//add eax, 1 ;increment
//add edx, ecx ;add size (indirection vector)
//jmp :loop ;jump back
//:done
IA32_Mov_Reg_Rm(jit, REG_EDX, AMX_REG_STK, MOD_MEM_REG);
IA32_Mov_Reg_Imm32(jit, REG_EAX, 1);
jitoffs_t loop1 = jit->get_outputpos();
IA32_Cmp_Reg_Rm_ESP(jit, REG_EAX);
jitoffs_t done1 = IA32_Jump_Cond_Imm8(jit, CC_AE, 0);
IA32_Mov_Reg_Rm_Disp_Reg(jit, REG_ECX, AMX_REG_STK, REG_EAX, SCALE4);
IA32_IMul_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
IA32_Add_Rm_Imm8(jit, REG_EAX, 1, MOD_REG);
IA32_Add_Reg_Rm(jit, REG_EDX, REG_ECX, MOD_REG);
IA32_Write_Jump8(jit, IA32_Jump_Imm8(jit, loop1), loop1);
IA32_Send_Jump8_Here(jit, done1);
/* Test if we have heap space for this */
//mov eax, [esi+info.heap] ;get heap pointer
//lea eax, [eax+edx*4] ;new heap pointer
//cmp eax, [esi+info.datasz] ;compare to heap low
//jbe :error ;die if we hit this (it should always be >)
//add eax, ebp ;relocate to stack
//cmp eax, edi ;die if above the stack pointer
//jae :error
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
IA32_Lea_Reg_DispRegMult(jit, REG_EAX, REG_EAX, REG_EDX, SCALE4);
IA32_Cmp_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_DATASIZE);
err1 = IA32_Jump_Cond_Imm32(jit, CC_BE, 0);
IA32_Add_Reg_Rm(jit, REG_EAX, AMX_REG_DAT, MOD_REG);
IA32_Cmp_Reg_Rm(jit, REG_EAX, AMX_REG_STK, MOD_REG);
err2 = IA32_Jump_Cond_Imm32(jit, CC_AE, 0);
/* Prepare for indirection iteration */
//mov eax, [esi+info.heap] ;get heap pointer
//lea ebx, [eax+edx*4] ;new heap pointer
//mov [esi+info.heap], ebx ;store back
//push eax ;save heap pointer - we need it
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_HEAP);
IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EDX, SCALE4);
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, REG_EBX, AMX_INFO_HEAP);
IA32_Push_Reg(jit, REG_EAX);
WriteOp_Tracker_Push_Reg(jit, REG_EDX);
/* This part is too messy to do in straight assembly.
* I'm letting the compiler handle it and thus it's in C.
*/
//lea ebx, [ebp+eax] ;get base pointer
//push dword [esp-8] ;push autozero
//push dword [esp-8] ;push dimension count
//push edi ;push dim array
//push ebx
//call GenerateArrayIndirectionVectors
//add esp, 4*4
IA32_Lea_Reg_DispRegMult(jit, REG_EBX, REG_EAX, REG_EBP, NOSCALE);
IA32_Push_Rm_Disp8_ESP(jit, 8);
IA32_Push_Rm_Disp8_ESP(jit, 8);
IA32_Push_Reg(jit, REG_EDI);
IA32_Push_Reg(jit, REG_EBX);
IA32_Write_Jump32_Abs(jit, IA32_Call_Imm32(jit, 0), (void *)&GenerateArrayIndirectionVectors);
IA32_Add_Rm_Imm8(jit, REG_ESP, 4*4, MOD_REG);
/* Store the heap pointer back into the stack */
//pop eax ;restore heap pointer
//pop ecx ;restore param count
//lea edi, [edi+ecx*4-4] ;pop params-4 off the stack
//mov [edi], eax ;store back the heap pointer
IA32_Pop_Reg(jit, REG_EAX);
IA32_Pop_Reg(jit, REG_ECX);
IA32_Lea_Reg_DispRegMultImm8(jit, AMX_REG_STK, AMX_REG_STK, REG_ECX, SCALE4, -4);
IA32_Mov_Rm_Reg(jit, AMX_REG_STK, REG_EAX, MOD_MEM_REG);
/* Return to caller */
//pop edx
//pop eax
//pop ebx
//ret
IA32_Pop_Reg(jit, REG_ECX);
IA32_Pop_Reg(jit, REG_EAX);
IA32_Pop_Reg(jit, REG_EBX);
IA32_Return(jit);
//:error
IA32_Send_Jump32_Here(jit, err1);
IA32_Send_Jump32_Here(jit, err2);
Write_SetError(jit, SP_ERROR_ARRAY_TOO_BIG);
}
void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg)
{
/* Save registers that may be damaged by the call */
//push eax
//push ecx
//push edi
//lea edi, [<reg>*4] ; we want the count in bytes not in cells
IA32_Push_Reg(jit, AMX_REG_PRI);
if (reg == REG_ECX)
{
IA32_Push_Reg(jit, AMX_REG_TMP);
}
IA32_Push_Reg(jit, AMX_REG_STK);
IA32_Lea_Reg_RegMultImm32(jit, REG_EDI, reg, SCALE4, 0);
/* Get the context ptr, push it and call the check */
//mov eax, [esi+context]
//push eax
//call JIT_VerifyOrAllocateTracker
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Push_Reg(jit, REG_EAX);
jitoffs_t call = IA32_Call_Imm32(jit, 0);
IA32_Write_Jump32_Abs(jit, call, (void *)JIT_VerifyOrAllocateTracker);
/* Check for errors */
//cmp eax, 0
//jnz :error
IA32_Cmp_Rm_Imm8(jit, MOD_REG, REG_EAX, 0);
IA32_Jump_Cond_Imm32_Abs(jit, CC_NZ, g_Jit.GetReturnPoint());
/* Restore */
//pop eax
IA32_Pop_Reg(jit, REG_EAX);
/* Push the register into the stack and increment pCur */
//mov edx, [eax+vm[]]
//mov eax, [edx+pcur]
//add [edx+pcur], 4
//mov [eax], edi
IA32_Mov_Reg_Rm_Disp8(jit, REG_EDX, REG_EAX, offsetof(sp_context_t, vm[JITVARS_TRACKER]));
IA32_Mov_Reg_Rm_Disp8(jit, REG_EAX, REG_EDX, offsetof(tracker_t, pCur));
IA32_Add_Rm_Imm8_Disp8(jit, REG_EDX, 4, offsetof(tracker_t, pCur));
IA32_Mov_Rm_Reg(jit, REG_EAX, REG_EDI, MOD_MEM_REG);
/* Restore PRI, ALT and STK */
//pop edi
//pop ecx
//pop eax
IA32_Pop_Reg(jit, AMX_REG_STK);
if (reg == REG_ECX)
{
IA32_Pop_Reg(jit, AMX_REG_TMP);
}
IA32_Pop_Reg(jit, AMX_REG_PRI);
}
int JIT_VerifyOrAllocateTracker(sp_context_t *ctx)
{
tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
if ((size_t)(trk->pCur - trk->pBase) >= trk->size)
{
return SP_ERROR_TRACKER_BOUNDS;
}
if (trk->pCur+1 - (trk->pBase + trk->size) == 0)
{
size_t disp = trk->size - 1;
trk->size *= 2;
trk->pBase = (ucell_t *)realloc(trk->pBase, trk->size * sizeof(cell_t));
if (!trk->pBase)
{
return SP_ERROR_TRACKER_BOUNDS;
}
trk->pCur = trk->pBase + disp;
}
return SP_ERROR_NONE;
}
int JIT_VerifyLowBoundTracker(sp_context_t *ctx)
{
tracker_t *trk = (tracker_t *)(ctx->vm[JITVARS_TRACKER]);
if (trk->pCur <= trk->pBase)
{
return SP_ERROR_TRACKER_BOUNDS;
}
return SP_ERROR_NONE;
}
void AlignMe(JitWriter *jit)
{
jitoffs_t cur_offs = jit->get_outputpos();
jitoffs_t offset = ((cur_offs & 0xFFFFFFF0) + 16) - cur_offs;
if (offset)
{
for (jit_uint32_t i=0; i<offset; i++)
{
jit->write_ubyte(IA32_INT3);
}
}
}

View File

@ -1,317 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* 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_JIT_X86_OPCODE_INFO_H_
#define _INCLUDE_SOURCEPAWN_JIT_X86_OPCODE_INFO_H_
#include "../jit_helpers.h"
/**
* This outputs the execution function for a plugin.
* It also returns the 'return' offset, which is used for
* breaking out of the JIT during runtime.
*/
jitoffs_t Write_Execute_Function(JitWriter *jit);
/**
* Writes the Sysreq.* opcodes as a function call.
*/
void WriteOp_Sysreq_C_Function(JitWriter *jit);
/**
* Write the GENARRAY intrinsic function.
*/
void WriteIntrinsic_GenArray(JitWriter *jit);
void Write_Check_VerifyAddr(JitWriter *jit, jit_uint8_t reg);
/**
* Generates code to set an error state in the VM and return.
* This is used for generating the error set points in the VM.
* GetError writes the error from the context. SetError hardcodes.
*/
void Write_GetError(JitWriter *jit);
void Write_SetError(JitWriter *jit, int error);
/**
* Checks the stacks for min and low errors.
* :TODO: Should a variation of this go in the pushN opcodes?
*/
void Write_CheckStack_Min(JitWriter *jit);
void Write_CheckStack_Low(JitWriter *jit);
/**
* Checks the heap for min and low errors.
*/
void Write_CheckHeap_Min(JitWriter *jit);
void Write_CheckHeap_Low(JitWriter *jit);
/**
* Checks for division by zero.
*/
void Write_Check_DivZero(JitWriter *jit, jit_uint8_t reg);
/**
* Writes the break debug function.
*/
void Write_BreakDebug(JitWriter *jit);
/**
* These are for writing the PushN opcodes.
*/
void Macro_PushN_Addr(JitWriter *jit, int i);
void Macro_PushN_S(JitWriter *jit, int i);
void Macro_PushN_C(JitWriter *jit, int i);
void Macro_PushN(JitWriter *jit, int i);
/**
* Bound checking for the tracker stack,
*/
int JIT_VerifyLowBoundTracker(sp_context_t *ctx);
int JIT_VerifyOrAllocateTracker(sp_context_t *ctx);
/**
* Writes the push into tracker function.
*/
void WriteOp_Tracker_Push_Reg(JitWriter *jit, uint8_t reg);
/**
* Writes the rounding table for the float compare opcode.
*/
void Write_RoundingTable(JitWriter *jit);
/**
* Aligns the current code position to 16 bytes.
*/
void AlignMe(JitWriter *jit);
/**
* Legend for Statuses:
* ****** *** ********
* DONE -> code generation is done
* !GEN -> code generation is deliberate skipped because:
* (default): compiler does not generate
* DEPRECATED: this feature no longer exists/supported
* UNSUPPORTED: this opcode is not supported
* TODO: done in case needed
* VERIFIED -> code generation is checked as run-time working. prefixes:
* ! errors are not checked yet.
* - non-inline errors are not checked yet.
* ~ assumed checked because of related variation, but not actually checked
*/
typedef enum
{
OP_NONE, /* invalid opcode */
OP_LOAD_PRI, //!VERIFIED
OP_LOAD_ALT, //~!VERIFIED (load.pri)
OP_LOAD_S_PRI, //VERIFIED
OP_LOAD_S_ALT, //VERIFIED
OP_LREF_PRI, //VERIFIED
OP_LREF_ALT, //~VERIFIED
OP_LREF_S_PRI, //VERIFIED
OP_LREF_S_ALT, //~VERIFIED (lref.s.pri)
OP_LOAD_I, //VERIFIED
OP_LODB_I, //VERIFIED
OP_CONST_PRI, //VERIFIED
OP_CONST_ALT, //~VERIFIED (const.pri)
OP_ADDR_PRI, //VERIFIED
OP_ADDR_ALT, //VERIFIED
OP_STOR_PRI, //VERIFIED
OP_STOR_ALT, //~VERIFIED (stor.pri)
OP_STOR_S_PRI, //VERIFIED
OP_STOR_S_ALT, //~VERIFIED (stor.s.pri)
OP_SREF_PRI, //VERIFIED
OP_SREF_ALT, //~VERIFIED
OP_SREF_S_PRI, //VERIFIED
OP_SREF_S_ALT, //~VERIFIED (stor.s.alt)
OP_STOR_I, //VERIFIED
OP_STRB_I, //VERIFIED
OP_LIDX, //VERIFIED
OP_LIDX_B, //DONE
OP_IDXADDR, //VERIFIED
OP_IDXADDR_B, //DONE
OP_ALIGN_PRI, // !GEN :TODO: - only used for pack access, drop support in compiler first
OP_ALIGN_ALT, // !GEN :TODO: - only used for pack access, drop support in compiler first
OP_LCTRL, // !GEN
OP_SCTRL, // !GEN
OP_MOVE_PRI, //~VERIFIED (move.alt)
OP_MOVE_ALT, //VERIFIED
OP_XCHG, //DONE
OP_PUSH_PRI, //DONE
OP_PUSH_ALT, //DONE
OP_PUSH_R, // !GEN DEPRECATED
OP_PUSH_C, //VERIFIED
OP_PUSH, //DONE
OP_PUSH_S, //VERIFIED
OP_POP_PRI, //VERIFIED
OP_POP_ALT, //VERIFIED
OP_STACK, //VERIFIED
OP_HEAP, //VERIFIED
OP_PROC, //VERIFIED
OP_RET, // !GEN
OP_RETN, //VERIFIED
OP_CALL, //VERIFIED
OP_CALL_PRI, // !GEN
OP_JUMP, //VERIFIED
OP_JREL, // !GEN
OP_JZER, //VERIFIED
OP_JNZ, //DONE
OP_JEQ, //VERIFIED
OP_JNEQ, //VERIFIED
OP_JLESS, // !GEN
OP_JLEQ, // !GEN
OP_JGRTR, // !GEN
OP_JGEQ, // !GEN
OP_JSLESS, //VERIFIED
OP_JSLEQ, //VERIFIED
OP_JSGRTR, //VERIFIED
OP_JSGEQ, //VERIFIED
OP_SHL, //VERIFIED
OP_SHR, //VERIFIED (Note: operator >>>)
OP_SSHR, //VERIFIED (Note: operator >>)
OP_SHL_C_PRI, //DONE
OP_SHL_C_ALT, //DONE
OP_SHR_C_PRI, //DONE
OP_SHR_C_ALT, //DONE
OP_SMUL, //VERIFIED
OP_SDIV, //DONE
OP_SDIV_ALT, //VERIFIED
OP_UMUL, // !GEN
OP_UDIV, // !GEN
OP_UDIV_ALT, // !GEN
OP_ADD, //VERIFIED
OP_SUB, //DONE
OP_SUB_ALT, //VERIFIED
OP_AND, //VERIFIED
OP_OR, //VERIFIED
OP_XOR, //VERIFIED
OP_NOT, //VERIFIED
OP_NEG, //VERIFIED
OP_INVERT, //VERIFIED
OP_ADD_C, //VERIFIED
OP_SMUL_C, //VERIFIED
OP_ZERO_PRI, //VERIFIED
OP_ZERO_ALT, //~VERIFIED
OP_ZERO, //VERIFIED
OP_ZERO_S, //VERIFIED
OP_SIGN_PRI, //DONE
OP_SIGN_ALT, //DONE
OP_EQ, //VERIFIED
OP_NEQ, //VERIFIED
OP_LESS, // !GEN
OP_LEQ, // !GEN
OP_GRTR, // !GEN
OP_GEQ, // !GEN
OP_SLESS, //VERIFIED
OP_SLEQ, //VERIFIED
OP_SGRTR, //VERIFIED
OP_SGEQ, //VERIFIED
OP_EQ_C_PRI, //DONE
OP_EQ_C_ALT, //DONE
OP_INC_PRI, //VERIFIED
OP_INC_ALT, //~VERIFIED (inc.pri)
OP_INC, //VERIFIED
OP_INC_S, //VERIFIED
OP_INC_I, //VERIFIED
OP_DEC_PRI, //VERIFIED
OP_DEC_ALT, //~VERIFIED (dec.pri)
OP_DEC, //VERIFIED
OP_DEC_S, //VERIFIED
OP_DEC_I, //VERIFIED
OP_MOVS, //VERIFIED
OP_CMPS, // !GEN
OP_FILL, //VERIFIED
OP_HALT, //DONE
OP_BOUNDS, //VERIFIED
OP_SYSREQ_PRI, // !GEN
OP_SYSREQ_C, //VERIFIED
OP_FILE, // !GEN DEPRECATED
OP_LINE, // !GEN DEPRECATED
OP_SYMBOL, // !GEN DEPRECATED
OP_SRANGE, // !GEN DEPRECATED
OP_JUMP_PRI, // !GEN
OP_SWITCH, //VERIFIED
OP_CASETBL, //VERIFIED
OP_SWAP_PRI, //VERIFIED
OP_SWAP_ALT, //~VERIFIED (swap.alt)
OP_PUSH_ADR, //VERIFIED
OP_NOP, //VERIFIED (lol)
OP_SYSREQ_N, //VERIFIED
OP_SYMTAG, // !GEN DEPRECATED
OP_BREAK, //DONE
OP_PUSH2_C, //~VERIFIED (push3.c)
OP_PUSH2, //VERIFIED
OP_PUSH2_S, //VERIFIED
OP_PUSH2_ADR, //VERIFIED
OP_PUSH3_C, //VERIFIED
OP_PUSH3, //~VERIFIED (push2)
OP_PUSH3_S, //~VERIFIED (push2.s)
OP_PUSH3_ADR, //~VERIFIED (push2.adr)
OP_PUSH4_C, //~VERIFIED (push3.c)
OP_PUSH4, //~VERIFIED (push2)
OP_PUSH4_S, //~VERIFIED (push2.s)
OP_PUSH4_ADR, //~VERIFIED (push2.adr)
OP_PUSH5_C, //~VERIFIED (push3.c)
OP_PUSH5, //~VERIFIED (push2)
OP_PUSH5_S, //~VERIFIED (push2.s)
OP_PUSH5_ADR, //~VERIFIED (push2.adr)
OP_LOAD_BOTH, //VERIFIED
OP_LOAD_S_BOTH, //VERIFIED
OP_CONST, //VERIFIED
OP_CONST_S, //DONE
/* ----- */
OP_SYSREQ_D, // !GEN UNSUPPORT
OP_SYSREQ_ND, // !GEN UNSUPPORT
/* ----- */
OP_TRACKER_PUSH_C, //DONE
OP_TRACKER_POP_SETHEAP, //VERIFIED
OP_GENARRAY, //VERIFIED
OP_GENARRAY_Z, //-VERIFIED (not tested for 1D arrays)
OP_STRADJUST_PRI, //VERIFIED
OP_STACKADJUST, //:TODO: VERIFY
OP_ENDPROC, //VERIFIED
OP_FABS, //VERIFIED
OP_FLOAT, //VERIFIED
OP_FLOATADD, //VERIFIED
OP_FLOATSUB, //VERIFIED
OP_FLOATMUL, //VERIFIED
OP_FLOATDIV, //VERIFIED
OP_RND_TO_NEAREST, //VERIFIED
OP_RND_TO_FLOOR, //VERIFIED
OP_RND_TO_CEIL, //VERIFIED
OP_RND_TO_ZERO, //VERIFIED
OP_FLOATCMP, //VERIFIED
/* ----- */
OP_NUM_OPCODES
} OPCODE;
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_OPCODE_INFO_H_

View File

@ -1,779 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* 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$
*/
case OP_MOVE_PRI:
{
WriteOp_Move_Pri(jit);
break;
}
case OP_MOVE_ALT:
{
WriteOp_Move_Alt(jit);
break;
}
case OP_XCHG:
{
WriteOp_Xchg(jit);
break;
}
case OP_PUSH:
{
WriteOp_Push(jit);
break;
}
case OP_PUSH_S:
{
WriteOp_Push_S(jit);
break;
}
case OP_PUSH2_C:
{
WriteOp_Push2_C(jit);
break;
}
case OP_PUSH3_C:
{
WriteOp_Push3_C(jit);
break;
}
case OP_PUSH4_C:
{
WriteOp_Push4_C(jit);
break;
}
case OP_PUSH5_C:
{
WriteOp_Push5_C(jit);
break;
}
case OP_PUSH2_ADR:
{
WriteOp_Push2_Adr(jit);
break;
}
case OP_PUSH3_ADR:
{
WriteOp_Push3_Adr(jit);
break;
}
case OP_PUSH4_ADR:
{
WriteOp_Push4_Adr(jit);
break;
}
case OP_PUSH5_ADR:
{
WriteOp_Push5_Adr(jit);
break;
}
case OP_PUSH2_S:
{
WriteOp_Push2_S(jit);
break;
}
case OP_PUSH3_S:
{
WriteOp_Push3_S(jit);
break;
}
case OP_PUSH4_S:
{
WriteOp_Push4_S(jit);
break;
}
case OP_PUSH5_S:
{
WriteOp_Push5_S(jit);
break;
}
case OP_PUSH5:
{
WriteOp_Push5(jit);
break;
}
case OP_PUSH4:
{
WriteOp_Push4(jit);
break;
}
case OP_PUSH3:
{
WriteOp_Push3(jit);
break;
}
case OP_PUSH2:
{
WriteOp_Push2(jit);
break;
}
case OP_ZERO_PRI:
{
WriteOp_Zero_Pri(jit);
break;
}
case OP_ZERO_ALT:
{
WriteOp_Zero_Alt(jit);
break;
}
case OP_PROC:
{
WriteOp_Proc(jit);
break;
}
case OP_SHL:
{
WriteOp_Shl(jit);
break;
}
case OP_SHR:
{
WriteOp_Shr(jit);
break;
}
case OP_SSHR:
{
WriteOp_Sshr(jit);
break;
}
case OP_SHL_C_PRI:
{
WriteOp_Shl_C_Pri(jit);
break;
}
case OP_SHL_C_ALT:
{
WriteOp_Shl_C_Alt(jit);
break;
}
case OP_SHR_C_PRI:
{
WriteOp_Shr_C_Pri(jit);
break;
}
case OP_SHR_C_ALT:
{
WriteOp_Shr_C_Alt(jit);
break;
}
case OP_SMUL:
{
WriteOp_SMul(jit);
break;
}
case OP_ADD:
{
WriteOp_Add(jit);
break;
}
case OP_SUB:
{
WriteOp_Sub(jit);
break;
}
case OP_SUB_ALT:
{
WriteOp_Sub_Alt(jit);
break;
}
case OP_NOP:
{
/* do nothing */
break;
}
case OP_NOT:
{
WriteOp_Not(jit);
break;
}
case OP_NEG:
{
WriteOp_Neg(jit);
break;
}
case OP_XOR:
{
WriteOp_Xor(jit);
break;
}
case OP_OR:
{
WriteOp_Or(jit);
break;
}
case OP_AND:
{
WriteOp_And(jit);
break;
}
case OP_INVERT:
{
WriteOp_Invert(jit);
break;
}
case OP_ADD_C:
{
WriteOp_Add_C(jit);
break;
}
case OP_SMUL_C:
{
WriteOp_SMul_C(jit);
break;
}
case OP_SIGN_PRI:
{
WriteOp_Sign_Pri(jit);
break;
}
case OP_SIGN_ALT:
{
WriteOp_Sign_Alt(jit);
break;
}
case OP_EQ:
{
WriteOp_Eq(jit);
break;
}
case OP_NEQ:
{
WriteOp_Neq(jit);
break;
}
case OP_SLESS:
{
WriteOp_Sless(jit);
break;
}
case OP_SLEQ:
{
WriteOp_Sleq(jit);
break;
}
case OP_SGRTR:
{
WriteOp_Sgrtr(jit);
break;
}
case OP_SGEQ:
{
WriteOp_Sgeq(jit);
break;
}
case OP_EQ_C_PRI:
{
WriteOp_Eq_C_Pri(jit);
break;
}
case OP_EQ_C_ALT:
{
WriteOp_Eq_C_Alt(jit);
break;
}
case OP_INC_PRI:
{
WriteOp_Inc_Pri(jit);
break;
}
case OP_INC_ALT:
{
WriteOp_Inc_Alt(jit);
break;
}
case OP_INC:
{
WriteOp_Inc(jit);
break;
}
case OP_INC_S:
{
WriteOp_Inc_S(jit);
break;
}
case OP_INC_I:
{
WriteOp_Inc_I(jit);
break;
}
case OP_DEC_PRI:
{
WriteOp_Dec_Pri(jit);
break;
}
case OP_DEC_ALT:
{
WriteOp_Dec_Alt(jit);
break;
}
case OP_DEC:
{
WriteOp_Dec(jit);
break;
}
case OP_DEC_S:
{
WriteOp_Dec_S(jit);
break;
}
case OP_DEC_I:
{
WriteOp_Dec_I(jit);
break;
}
case OP_LOAD_PRI:
{
WriteOp_Load_Pri(jit);
break;
}
case OP_LOAD_ALT:
{
WriteOp_Load_Alt(jit);
break;
}
case OP_LOAD_S_PRI:
{
WriteOp_Load_S_Pri(jit);
break;
}
case OP_LOAD_S_ALT:
{
WriteOp_Load_S_Alt(jit);
break;
}
case OP_LREF_PRI:
{
WriteOp_Lref_Pri(jit);
break;
}
case OP_LREF_ALT:
{
WriteOp_Lref_Alt(jit);
break;
}
case OP_LREF_S_PRI:
{
WriteOp_Lref_S_Pri(jit);
break;
}
case OP_LREF_S_ALT:
{
WriteOp_Lref_S_Alt(jit);
break;
}
case OP_CONST_PRI:
{
WriteOp_Const_Pri(jit);
break;
}
case OP_CONST_ALT:
{
WriteOp_Const_Alt(jit);
break;
}
case OP_ADDR_PRI:
{
WriteOp_Addr_Pri(jit);
break;
}
case OP_ADDR_ALT:
{
WriteOp_Addr_Alt(jit);
break;
}
case OP_STOR_PRI:
{
WriteOp_Stor_Pri(jit);
break;
}
case OP_STOR_ALT:
{
WriteOp_Stor_Alt(jit);
break;
}
case OP_STOR_S_PRI:
{
WriteOp_Stor_S_Pri(jit);
break;
}
case OP_STOR_S_ALT:
{
WriteOp_Stor_S_Alt(jit);
break;
}
case OP_IDXADDR:
{
WriteOp_Idxaddr(jit);
break;
}
case OP_SREF_PRI:
{
WriteOp_Sref_Pri(jit);
break;
}
case OP_SREF_ALT:
{
WriteOp_Sref_Alt(jit);
break;
}
case OP_SREF_S_PRI:
{
WriteOp_Sref_S_Pri(jit);
break;
}
case OP_SREF_S_ALT:
{
WriteOp_Sref_S_Alt(jit);
break;
}
case OP_POP_PRI:
{
WriteOp_Pop_Pri(jit);
break;
}
case OP_POP_ALT:
{
WriteOp_Pop_Alt(jit);
break;
}
case OP_SWAP_PRI:
{
WriteOp_Swap_Pri(jit);
break;
}
case OP_SWAP_ALT:
{
WriteOp_Swap_Alt(jit);
break;
}
case OP_PUSH_ADR:
{
WriteOp_PushAddr(jit);
break;
}
case OP_MOVS:
{
WriteOp_Movs(jit);
break;
}
case OP_FILL:
{
WriteOp_Fill(jit);
break;
}
case OP_PUSH_C:
{
WriteOp_Push_C(jit);
break;
}
case OP_ZERO:
{
WriteOp_Zero(jit);
break;
}
case OP_ZERO_S:
{
WriteOp_Zero_S(jit);
break;
}
case OP_PUSH_PRI:
{
WriteOp_Push_Pri(jit);
break;
}
case OP_PUSH_ALT:
{
WriteOp_Push_Alt(jit);
break;
}
case OP_LOAD_BOTH:
{
WriteOp_Load_Both(jit);
break;
}
case OP_LOAD_S_BOTH:
{
WriteOp_Load_S_Both(jit);
break;
}
case OP_CONST:
{
WriteOp_Const(jit);
break;
}
case OP_CONST_S:
{
WriteOp_Const_S(jit);
break;
}
case OP_LOAD_I:
{
WriteOp_Load_I(jit);
break;
}
case OP_LODB_I:
{
WriteOp_Lodb_I(jit);
break;
}
case OP_STOR_I:
{
WriteOp_Stor_I(jit);
break;
}
case OP_STRB_I:
{
WriteOp_Strb_I(jit);
break;
}
case OP_LIDX:
{
WriteOp_Lidx(jit);
break;
}
case OP_LIDX_B:
{
WriteOp_Lidx_B(jit);
break;
}
case OP_IDXADDR_B:
{
WriteOp_Idxaddr_B(jit);
break;
}
case OP_STACK:
{
WriteOp_Stack(jit);
break;
}
case OP_HEAP:
{
WriteOp_Heap(jit);
break;
}
case OP_SDIV:
{
WriteOp_SDiv(jit);
break;
}
case OP_SDIV_ALT:
{
WriteOp_SDiv_Alt(jit);
break;
}
case OP_RETN:
{
WriteOp_Retn(jit);
break;
}
case OP_BOUNDS:
{
WriteOp_Bounds(jit);
break;
}
case OP_HALT:
{
WriteOp_Halt(jit);
break;
}
case OP_BREAK:
{
WriteOp_Break(jit);
break;
}
case OP_JUMP:
{
WriteOp_Jump(jit);
break;
}
case OP_JZER:
{
WriteOp_Jzer(jit);
break;
}
case OP_JNZ:
{
WriteOp_Jnz(jit);
break;
}
case OP_JEQ:
{
WriteOp_Jeq(jit);
break;
}
case OP_JNEQ:
{
WriteOp_Jneq(jit);
break;
}
case OP_JSLESS:
{
WriteOp_Jsless(jit);
break;
}
case OP_JSGRTR:
{
WriteOp_JsGrtr(jit);
break;
}
case OP_JSGEQ:
{
WriteOp_JsGeq(jit);
break;
}
case OP_JSLEQ:
{
WriteOp_Jsleq(jit);
break;
}
case OP_SWITCH:
{
WriteOp_Switch(jit);
break;
}
case OP_CASETBL:
{
WriteOp_Casetbl(jit);
break;
}
case OP_CALL:
{
WriteOp_Call(jit);
break;
}
case OP_SYSREQ_C:
{
WriteOp_Sysreq_C(jit);
break;
}
case OP_SYSREQ_N:
{
WriteOp_Sysreq_N(jit);
break;
}
case OP_TRACKER_PUSH_C:
{
WriteOp_Tracker_Push_C(jit);
break;
}
case OP_TRACKER_POP_SETHEAP:
{
WriteOp_Tracker_Pop_SetHeap(jit);
break;
}
case OP_GENARRAY:
{
WriteOp_GenArray(jit, false);
break;
}
case OP_GENARRAY_Z:
{
WriteOp_GenArray(jit, true);
break;
}
case OP_STRADJUST_PRI:
{
WriteOp_Stradjust_Pri(jit);
break;
}
case OP_FABS:
{
WriteOp_FloatAbs(jit);
break;
}
case OP_FLOAT:
{
WriteOp_Float(jit);
break;
}
case OP_FLOATADD:
{
WriteOp_FloatAdd(jit);
break;
}
case OP_FLOATSUB:
{
WriteOp_FloatSub(jit);
break;
}
case OP_FLOATMUL:
{
WriteOp_FloatMul(jit);
break;
}
case OP_FLOATDIV:
{
WriteOp_FloatDiv(jit);
break;
}
case OP_RND_TO_NEAREST:
{
WriteOp_RountToNearest(jit);
break;
}
case OP_RND_TO_FLOOR:
{
WriteOp_RoundToFloor(jit);
break;
}
case OP_RND_TO_ZERO:
{
WriteOp_RoundToZero(jit);
break;
}
case OP_RND_TO_CEIL:
{
WriteOp_RoundToCeil(jit);
break;
}
case OP_FLOATCMP:
{
WriteOp_FloatCompare(jit);
break;
}
case OP_STACKADJUST:
{
WriteOp_StackAdjust(jit);
break;
}
case OP_ENDPROC:
{
WriteOp_EndProc(jit);
break;
}
#if defined USE_UNGEN_OPCODES
#include "ungen_opcode_switch.inc"
#endif
default:
{
data->error_set = SP_ERROR_INVALID_INSTRUCTION;
break;
}

View File

@ -1,121 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* 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$
*/
case OP_UMUL:
{
WriteOp_UMul(jit);
break;
}
case OP_LESS:
{
WriteOp_Less(jit);
break;
}
case OP_LEQ:
{
WriteOp_Leq(jit);
break;
}
case OP_GRTR:
{
WriteOp_Grtr(jit);
break;
}
case OP_GEQ:
{
WriteOp_Geq(jit);
break;
}
case OP_ALIGN_PRI:
{
WriteOp_Align_Pri(jit);
break;
}
case OP_ALIGN_ALT:
{
WriteOp_Align_Alt(jit);
break;
}
case OP_LCTRL:
{
WriteOp_Lctrl(jit);
break;
}
case OP_SCTRL:
{
WriteOp_Sctrl(jit);
break;
}
case OP_UDIV:
{
WriteOp_UDiv(jit);
break;
}
case OP_UDIV_ALT:
{
WriteOp_UDiv_Alt(jit);
break;
}
case OP_RET:
{
WriteOp_Ret(jit);
break;
}
case OP_CMPS:
{
WriteOp_Cmps(jit);
break;
}
case OP_JREL:
{
WriteOp_JRel(jit);
break;
}
case OP_JLESS:
{
WriteOp_Jless(jit);
break;
}
case OP_JLEQ:
{
WriteOp_Jeq(jit);
break;
}
case OP_JGRTR:
{
WriteOp_Jgrtr(jit);
break;
}
case OP_JGEQ:
{
WriteOp_Jgeq(jit);
break;
}

View File

@ -1,325 +0,0 @@
/**
* vim: set ts=4 :
* =============================================================================
* SourceMod
* 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_JIT_X86_UNGEN_OPCODES_H_
#define _INCLUDE_SOURCEPAWN_JIT_X86_UNGEN_OPCODES_H_
inline void WriteOp_UMul(JitWriter *jit)
{
//mov ecx, edx
//mul edx
//mov edx, ecx
IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_ALT, MOD_REG);
IA32_Mul_Rm(jit, AMX_REG_ALT, MOD_REG);
IA32_Mov_Reg_Rm(jit, AMX_REG_ALT, AMX_REG_TMP, MOD_REG);
}
inline void WriteOp_Less(JitWriter *jit)
{
//cmp eax, edx ; PRI < ALT ? (unsigned)
//mov eax, 0
//setb al
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Mov_Reg_Imm32(jit, AMX_REG_PRI, 0);
IA32_SetCC_Rm8(jit, AMX_REG_PRI, CC_B);
}
inline void WriteOp_Leq(JitWriter *jit)
{
//cmp eax, edx ; PRI <= ALT ? (unsigned)
//mov eax, 0
//setbe al
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Mov_Reg_Imm32(jit, AMX_REG_PRI, 0);
IA32_SetCC_Rm8(jit, AMX_REG_PRI, CC_BE);
}
inline void WriteOp_Grtr(JitWriter *jit)
{
//cmp eax, edx ; PRI > ALT ? (unsigned)
//mov eax, 0
//seta al
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Mov_Reg_Imm32(jit, AMX_REG_PRI, 0);
IA32_SetCC_Rm8(jit, AMX_REG_PRI, CC_A);
}
inline void WriteOp_Geq(JitWriter *jit)
{
//cmp eax, edx ; PRI >= ALT ? (unsigned)
//mov eax, 0
//setae al
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Mov_Reg_Imm32(jit, AMX_REG_PRI, 0);
IA32_SetCC_Rm8(jit, AMX_REG_PRI, CC_AE);
}
inline void WriteOp_Align_Pri(JitWriter *jit)
{
//xor eax, <cellsize - val>
cell_t val = sizeof(cell_t) - jit->read_cell();
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
{
IA32_Xor_Rm_Imm8(jit, AMX_REG_PRI, MOD_REG, (jit_int8_t)val);
} else {
IA32_Xor_Eax_Imm32(jit, val);
}
}
inline void WriteOp_Align_Alt(JitWriter *jit)
{
//xor edx, <cellsize - val>
cell_t val = sizeof(cell_t) - jit->read_cell();
if (val <= SCHAR_MAX && val >= SCHAR_MIN)
{
IA32_Xor_Rm_Imm8(jit, AMX_REG_ALT, MOD_REG, (jit_int8_t)val);
} else {
IA32_Xor_Rm_Imm32(jit, AMX_REG_ALT, MOD_REG, val);
}
}
inline void WriteOp_Cmps(JitWriter *jit)
{
//push edi
//push esi
//lea esi, [ebp+edx]
//lea edi, [ebp+eax]
//mov ecx, <val>
unsigned int val = jit->read_cell();
IA32_Push_Reg(jit, REG_EDI);
IA32_Push_Reg(jit, REG_ESI);
IA32_Lea_Reg_DispEBPRegMult(jit, REG_ESI, AMX_REG_DAT, AMX_REG_ALT, NOSCALE);
IA32_Lea_Reg_DispEBPRegMult(jit, REG_EDI, AMX_REG_DAT, AMX_REG_PRI, NOSCALE);
IA32_Mov_Reg_Imm32(jit, REG_ECX, val);
//xor eax, eax
//repe cmpsb
//je :cmps1
IA32_Xor_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_PRI, MOD_REG);
IA32_Rep(jit);
IA32_Cmpsb(jit);
jitoffs_t jmp = IA32_Jump_Cond_Imm8(jit, CC_E, 0);
//sbb eax, eax
//sbb eax, -1
IA32_Sbb_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_PRI, MOD_REG);
IA32_Sbb_Rm_Imm8(jit, AMX_REG_PRI, -1, MOD_REG);
//:cmps1
//pop esi
//pop edi
IA32_Send_Jump8_Here(jit, jmp);
IA32_Pop_Reg(jit, REG_ESI);
IA32_Pop_Reg(jit, REG_EDI);
}
inline void WriteOp_Lctrl(JitWriter *jit)
{
cell_t val = jit->read_cell();
switch (val)
{
case 0:
{
//mov ecx, [esi+ctx]
//mov eax, [ecx+ctx.codebase]
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_PRI, AMX_REG_TMP, offsetof(sp_context_t, codebase));
break;
}
case 1:
{
//mov eax, ebp
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_DAT, MOD_REG);
break;
}
case 2:
{
//mov eax, [esi+hea]
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_PRI, AMX_REG_INFO, AMX_INFO_HEAP);
break;
}
case 3:
{
//mov ecx, [esi+ctx]
//mov eax, [ecx+ctx.memory]
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_TMP, AMX_REG_INFO, AMX_INFO_CONTEXT);
IA32_Mov_Reg_Rm_Disp8(jit, AMX_REG_PRI, AMX_REG_TMP, offsetof(sp_context_t, memory));
break;
}
case 4:
{
//mov eax, edi
//sub eax, ebp - unrelocate
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_STK, MOD_REG);
IA32_Sub_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_DAT, MOD_REG);
break;
}
case 5:
{
//mov eax, [esi+frm]
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_INFO, MOD_MEM_REG);
break;
}
case 6:
{
//mov eax, [cip]
jitoffs_t imm32 = IA32_Mov_Reg_Imm32(jit, AMX_REG_PRI, 0);
jitoffs_t save = jit->get_outputpos();
jit->set_outputpos(imm32);
jit->write_int32((uint32_t)(jit->outbase + save));
jit->set_outputpos(save);
break;
}
}
}
inline void WriteOp_Sctrl(JitWriter *jit)
{
cell_t val = jit->read_cell();
switch (val)
{
case 2:
{
//mov [esi+hea], eax
IA32_Mov_Rm_Reg_Disp8(jit, AMX_REG_INFO, AMX_REG_PRI, AMX_INFO_HEAP);
break;
}
case 4:
{
//lea edi, [ebp+eax]
IA32_Lea_Reg_DispEBPRegMult(jit, AMX_REG_STK, AMX_REG_DAT, AMX_REG_PRI, NOSCALE);
break;
}
case 5:
{
//lea ebx, [ebp+eax] - overwrite frm
//mov [esi+frm], eax - overwrite stacked frame
IA32_Lea_Reg_DispEBPRegMult(jit, AMX_REG_FRM, AMX_REG_DAT, AMX_REG_PRI, NOSCALE);
IA32_Mov_Rm_Reg(jit, AMX_REG_INFO, AMX_REG_PRI, MOD_MEM_REG);
break;
}
case 6:
{
IA32_Jump_Reg(jit, AMX_REG_PRI);
break;
}
}
}
inline void WriteOp_UDiv(JitWriter *jit)
{
//mov ecx, edx
//xor edx, edx
//div ecx
IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_ALT, MOD_REG);
IA32_Xor_Reg_Rm(jit, AMX_REG_ALT, AMX_REG_ALT, MOD_REG);
Write_Check_DivZero(jit, AMX_REG_TMP);
IA32_Div_Rm(jit, AMX_REG_TMP, MOD_REG);
}
inline void WriteOp_UDiv_Alt(JitWriter *jit)
{
//mov ecx, eax
//mov eax, edx
//xor edx, edx
//div ecx
IA32_Mov_Reg_Rm(jit, AMX_REG_TMP, AMX_REG_PRI, MOD_REG);
IA32_Mov_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Xor_Reg_Rm(jit, AMX_REG_ALT, AMX_REG_ALT, MOD_REG);
Write_Check_DivZero(jit, AMX_REG_TMP);
IA32_Div_Rm(jit, AMX_REG_TMP, MOD_REG);
}
inline void WriteOp_Ret(JitWriter *jit)
{
//mov ebx, [edi] - get old FRM
//add edi, 4 - pop stack
//mov [esi+frm], ebx - restore
//add ebx, ebp - relocate
//ret
IA32_Mov_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_STK, MOD_MEM_REG);
IA32_Add_Rm_Imm8(jit, AMX_REG_STK, 4, MOD_REG);
IA32_Mov_Rm_Reg(jit, AMX_REG_INFO, AMX_REG_FRM, MOD_MEM_REG);
IA32_Add_Reg_Rm(jit, AMX_REG_FRM, AMX_REG_DAT, MOD_REG);
IA32_Return(jit);
}
inline void WriteOp_JRel(JitWriter *jit)
{
//jmp <offs> ;relative jump
cell_t cip_offs = jit->read_cell();
/* Note that since code size calculation has to be done in the same
* phase as building relocation information, we cannot know the jump size
* beforehand. Thus, we always write full 32bit jumps for safety.
*/
jitoffs_t jmp = IA32_Jump_Imm32(jit, 0);
IA32_Write_Jump32(jit, jmp, RelocLookup(jit, cip_offs));
}
inline void WriteOp_Jless(JitWriter *jit)
{
//cmp eax, edx
//jb <target>
cell_t target = jit->read_cell();
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Jump_Cond_Imm32(jit, CC_B, RelocLookup(jit, target, false));
}
inline void WriteOp_Jleq(JitWriter *jit)
{
//cmp eax, edx
//jbe <target>
cell_t target = jit->read_cell();
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Jump_Cond_Imm32(jit, CC_BE, RelocLookup(jit, target, false));
}
inline void WriteOp_Jgrtr(JitWriter *jit)
{
//cmp eax, edx
//ja <target>
cell_t target = jit->read_cell();
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Jump_Cond_Imm32(jit, CC_A, RelocLookup(jit, target, false));
}
inline void WriteOp_Jgeq(JitWriter *jit)
{
//cmp eax, edx
//jae <target>
cell_t target = jit->read_cell();
IA32_Cmp_Reg_Rm(jit, AMX_REG_PRI, AMX_REG_ALT, MOD_REG);
IA32_Jump_Cond_Imm32(jit, CC_AE, RelocLookup(jit, target, false));
}
#endif //_INCLUDE_SOURCEPAWN_JIT_X86_UNGEN_OPCODES_H_