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
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -85,6 +85,8 @@ typedef uint32_t funcid_t; /**< Function index code */
#define SP_ERROR_ABORTED 25 /**< Function call was aborted */
#define SP_ERROR_CODE_TOO_OLD 26 /**< Code is too old for this VM */
#define SP_ERROR_CODE_TOO_NEW 27 /**< Code is too new for this VM */
#define SP_ERROR_OUT_OF_MEMORY 28 /**< Out of memory */
#define SP_ERROR_INTEGER_OVERFLOW 29 /**< Integer overflow (-INT_MIN / -1) */
//Hey you! Update the string table if you add to the end of me! */
/**********************************************

View File

@ -22,8 +22,8 @@ binary.AddSourceFiles('sourcepawn/jit', [
'sp_vm_basecontext.cpp',
'sp_vm_engine.cpp',
'sp_vm_function.cpp',
'opcodes.cpp',
'x86/jit_x86.cpp',
'x86/opcode_helpers.cpp',
'zlib/adler32.c',
'zlib/compress.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 <stdlib.h>
#include <string.h>
@ -12,573 +13,571 @@
using namespace SourcePawn;
BaseRuntime::BaseRuntime() : m_Debug(&m_plugin), m_pPlugin(&m_plugin), m_pCtx(NULL),
m_PubFuncs(NULL), m_PubJitFuncs(NULL), m_pCo(NULL), m_CompSerial(0)
static inline bool
IsPointerCellAligned(void *p)
{
memset(&m_plugin, 0, sizeof(m_plugin));
return uintptr_t(p) % 4 == 0;
}
m_FuncCache = NULL;
m_MaxFuncs = 0;
m_NumFuncs = 0;
memset(m_CodeHash, 0, sizeof(m_CodeHash));
memset(m_DataHash, 0, sizeof(m_DataHash));
BaseRuntime::BaseRuntime()
: m_Debug(&m_plugin),
m_pCtx(NULL),
m_PubFuncs(NULL),
m_PubJitFuncs(NULL),
co_(NULL),
m_CompSerial(0)
{
memset(&m_plugin, 0, sizeof(m_plugin));
m_MaxFuncs = 0;
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()
{
for (uint32_t i = 0; i < m_pPlugin->num_publics; i++)
{
delete m_PubFuncs[i];
m_PubFuncs[i] = NULL;
}
delete [] m_PubFuncs;
delete [] m_PubJitFuncs;
for (uint32_t i = 0; i < m_plugin.num_publics; i++)
delete m_PubFuncs[i];
delete [] m_PubFuncs;
delete [] m_PubJitFuncs;
delete [] float_table_;
delete [] function_map_;
delete [] alt_pcode_;
for (unsigned int i = 0; i < m_NumFuncs; i++)
{
delete m_FuncCache[i];
}
free(m_FuncCache);
for (size_t i = 0; i < m_JitFunctions.length(); i++)
delete m_JitFunctions[i];
delete m_pCtx;
if (m_pCo != NULL)
{
m_pCo->Abort();
}
delete m_pCtx;
if (co_)
co_->Abort();
free(m_pPlugin->base);
delete [] m_pPlugin->memory;
delete [] m_pPlugin->publics;
delete [] m_pPlugin->pubvars;
delete [] m_pPlugin->natives;
free(m_pPlugin->name);
free(m_plugin.base);
delete [] m_plugin.memory;
delete [] m_plugin.publics;
delete [] m_plugin.pubvars;
delete [] m_plugin.natives;
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)
{
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 set_err;
char *nameptr;
uint8_t sectnum = 0;
sp_plugin_t *plugin = m_pPlugin;
sp_file_section_t *secptr = (sp_file_section_t *)(base + sizeof(sp_file_hdr_t));
char *nameptr;
uint8_t sectnum = 0;
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;
plugin->base_size = hdr->imagesize;
set_err = SP_ERROR_NONE;
m_plugin.base = base;
m_plugin.base_size = hdr->imagesize;
if (hdr->version == 0x0101)
{
plugin->debug.unpacked = true;
}
if (hdr->version == 0x0101)
m_plugin.debug.unpacked = true;
/* We have to read the name section first */
for (sectnum = 0; sectnum < hdr->sections; sectnum++)
{
nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs);
if (strcmp(nameptr, ".names") == 0)
{
plugin->stringbase = (const char *)(base + secptr[sectnum].dataoffs);
break;
}
}
/* We have to read the name section first */
for (sectnum = 0; sectnum < hdr->sections; sectnum++) {
nameptr = (char *)(base + hdr->stringtab + secptr[sectnum].nameoffs);
if (strcmp(nameptr, ".names") == 0) {
m_plugin.stringbase = (const char *)(base + secptr[sectnum].dataoffs);
break;
}
}
sectnum = 0;
sectnum = 0;
/* Now read the rest of the sections */
while (sectnum < hdr->sections)
{
nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
/* Now read the rest of the sections */
while (sectnum < hdr->sections) {
nameptr = (char *)(base + hdr->stringtab + secptr->nameoffs);
if (!(plugin->pcode) && !strcmp(nameptr, ".code"))
{
sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
if (!(m_plugin.pcode) && !strcmp(nameptr, ".code")) {
sp_file_code_t *cod = (sp_file_code_t *)(base + secptr->dataoffs);
if (cod->codeversion < SP_CODEVERS_JIT1)
{
return SP_ERROR_CODE_TOO_OLD;
}
else if (cod->codeversion > SP_CODEVERS_JIT2)
{
return SP_ERROR_CODE_TOO_NEW;
}
if (cod->codeversion < SP_CODEVERS_JIT1)
return SP_ERROR_CODE_TOO_OLD;
if (cod->codeversion > SP_CODEVERS_JIT2)
return SP_ERROR_CODE_TOO_NEW;
plugin->pcode = base + secptr->dataoffs + cod->code;
plugin->pcode_size = cod->codesize;
plugin->flags = cod->flags;
plugin->pcode_version = cod->codeversion;
}
else if (!(plugin->data) && !strcmp(nameptr, ".data"))
{
sp_file_data_t *dat = (sp_file_data_t *)(base + secptr->dataoffs);
plugin->data = base + secptr->dataoffs + dat->data;
plugin->data_size = dat->datasize;
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;
m_plugin.pcode = base + secptr->dataoffs + cod->code;
m_plugin.pcode_size = cod->codesize;
m_plugin.flags = cod->flags;
m_plugin.pcode_version = cod->codeversion;
if (!IsPointerCellAligned(m_plugin.pcode)) {
// The JIT requires that pcode is cell-aligned, so if it's not, we
// remap the code segment to a new address.
alt_pcode_ = new uint8_t[m_plugin.pcode_size];
memcpy(alt_pcode_, m_plugin.pcode, m_plugin.pcode_size);
assert(IsPointerCellAligned(alt_pcode_));
publics = (sp_file_publics_t *)(base + secptr->dataoffs);
plugin->num_publics = secptr->size / sizeof(sp_file_publics_t);
m_plugin.pcode = alt_pcode_;
}
} 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)
{
plugin->publics = new sp_public_t[plugin->num_publics];
publics = (sp_file_publics_t *)(base + secptr->dataoffs);
m_plugin.num_publics = secptr->size / sizeof(sp_file_publics_t);
for (uint32_t i = 0; i < plugin->num_publics; i++)
{
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;
if (m_plugin.num_publics > 0) {
m_plugin.publics = new sp_public_t[m_plugin.num_publics];
pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
plugin->num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
for (uint32_t i = 0; i < m_plugin.num_publics; i++) {
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)
{
plugin->pubvars = new sp_pubvar_t[plugin->num_pubvars];
pubvars = (sp_file_pubvars_t *)(base + secptr->dataoffs);
m_plugin.num_pubvars = secptr->size / sizeof(sp_file_pubvars_t);
for (uint32_t i = 0; i < plugin->num_pubvars; i++)
{
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;
if (m_plugin.num_pubvars > 0) {
m_plugin.pubvars = new sp_pubvar_t[m_plugin.num_pubvars];
natives = (sp_file_natives_t *)(base + secptr->dataoffs);
plugin->num_natives = secptr->size / sizeof(sp_file_natives_t);
for (uint32_t i = 0; i < m_plugin.num_pubvars; i++) {
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)
{
plugin->natives = new sp_native_t[plugin->num_natives];
natives = (sp_file_natives_t *)(base + secptr->dataoffs);
m_plugin.num_natives = secptr->size / sizeof(sp_file_natives_t);
for (uint32_t i = 0; i < plugin->num_natives; i++)
{
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;
}
if (m_plugin.num_natives > 0) {
m_plugin.natives = new sp_native_t[m_plugin.num_natives];
secptr++;
sectnum++;
}
for (uint32_t i = 0; i < m_plugin.num_natives; i++) {
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)
{
return SP_ERROR_FILE_FORMAT;
}
secptr++;
sectnum++;
}
if ((plugin->flags & SP_FLAG_DEBUG) && (
!(plugin->debug.files) ||
!(plugin->debug.lines) ||
!(plugin->debug.symbols) ||
!(plugin->debug.stringbase) ))
{
return SP_ERROR_FILE_FORMAT;
}
if (m_plugin.pcode == NULL || m_plugin.data == NULL)
return SP_ERROR_FILE_FORMAT;
if (m_pPlugin->num_publics > 0)
{
m_PubFuncs = new CFunction *[m_pPlugin->num_publics];
memset(m_PubFuncs, 0, sizeof(CFunction *) * m_pPlugin->num_publics);
m_PubJitFuncs = new JitFunction *[m_pPlugin->num_publics];
memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_pPlugin->num_publics);
}
if ((m_plugin.flags & SP_FLAG_DEBUG) && (
!(m_plugin.debug.files) ||
!(m_plugin.debug.lines) ||
!(m_plugin.debug.symbols) ||
!(m_plugin.debug.stringbase) ))
{
return SP_ERROR_FILE_FORMAT;
}
MD5 md5_pcode;
md5_pcode.update(plugin->pcode, plugin->pcode_size);
md5_pcode.finalize();
md5_pcode.raw_digest(m_CodeHash);
MD5 md5_data;
md5_data.update(plugin->data, plugin->data_size);
md5_data.finalize();
md5_data.raw_digest(m_DataHash);
if (m_plugin.num_publics > 0) {
m_PubFuncs = new CFunction *[m_plugin.num_publics];
memset(m_PubFuncs, 0, sizeof(CFunction *) * m_plugin.num_publics);
m_PubJitFuncs = new JitFunction *[m_plugin.num_publics];
memset(m_PubJitFuncs, 0, sizeof(JitFunction *) * m_plugin.num_publics);
}
m_pPlugin->profiler = g_engine2.GetProfiler();
m_pCtx = new BaseContext(this);
m_pCo = g_Jit.StartCompilation(this);
MD5 md5_pcode;
md5_pcode.update(m_plugin.pcode, m_plugin.pcode_size);
md5_pcode.finalize();
md5_pcode.raw_digest(m_CodeHash);
MD5 md5_data;
md5_data.update(m_plugin.data, m_plugin.data_size);
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++)
{
if (strcmp(m_pPlugin->natives[i].name, name) == 0)
{
if (index)
{
*index = i;
}
return SP_ERROR_NONE;
}
}
uint32_t pcode_index = pcode_offset / 4;
assert(pcode_index < function_map_size_);
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)
{
return SP_ERROR_INDEX;
}
assert(pcode_offset % 4 == 0);
if (native)
{
*native = &(m_pPlugin->natives[index]);
}
uint32_t pcode_index = pcode_offset / 4;
assert(pcode_index < function_map_size_);
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;
uint32_t mid;
int diff, high, low;
uint32_t mid;
high = m_pPlugin->num_publics - 1;
low = 0;
high = m_plugin.num_publics - 1;
low = 0;
while (low <= high)
{
mid = (low + high) / 2;
diff = strcmp(m_pPlugin->publics[mid].name, name);
if (diff == 0)
{
if (index)
{
*index = mid;
}
return SP_ERROR_NONE;
} else if (diff < 0) {
low = mid + 1;
} else {
high = mid - 1;
}
}
while (low <= high) {
mid = (low + high) / 2;
diff = strcmp(m_plugin.publics[mid].name, name);
if (diff == 0) {
if (index)
*index = mid;
return SP_ERROR_NONE;
} 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)
{
return SP_ERROR_INDEX;
}
if (index >= m_plugin.num_publics)
return SP_ERROR_INDEX;
if (pblic)
{
*pblic = &(m_pPlugin->publics[index]);
}
if (pblic)
*pblic = &(m_plugin.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)
{
return SP_ERROR_INDEX;
}
if (index >= m_plugin.num_pubvars)
return SP_ERROR_INDEX;
if (pubvar)
{
*pubvar = &(m_pPlugin->pubvars[index]);
}
if (pubvar)
*pubvar = &(m_plugin.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;
uint32_t mid;
int diff, high, low;
uint32_t mid;
high = m_pPlugin->num_pubvars - 1;
low = 0;
high = m_plugin.num_pubvars - 1;
low = 0;
while (low <= high)
{
mid = (low + high) / 2;
diff = strcmp(m_pPlugin->pubvars[mid].name, name);
if (diff == 0)
{
if (index)
{
*index = mid;
}
return SP_ERROR_NONE;
}
else if (diff < 0)
{
low = mid + 1;
}
else
{
high = mid - 1;
}
}
while (low <= high) {
mid = (low + high) / 2;
diff = strcmp(m_plugin.pubvars[mid].name, name);
if (diff == 0) {
if (index)
*index = mid;
return SP_ERROR_NONE;
} 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)
{
return SP_ERROR_INDEX;
}
if (index >= m_plugin.num_pubvars)
return SP_ERROR_INDEX;
*local_addr = (uint8_t *)m_pPlugin->pubvars[index].offs - m_pPlugin->memory;
*phys_addr = m_pPlugin->pubvars[index].offs;
*local_addr = (uint8_t *)m_plugin.pubvars[index].offs - m_plugin.memory;
*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)
{
func_id >>= 1;
if (func_id >= m_pPlugin->num_publics)
{
return NULL;
}
pFunc = m_PubFuncs[func_id];
if (!pFunc)
{
m_PubFuncs[func_id] = new CFunction(this,
(func_id << 1) | 1,
func_id);
pFunc = m_PubFuncs[func_id];
}
}
if (func_id & 1) {
func_id >>= 1;
if (func_id >= m_plugin.num_publics)
return NULL;
pFunc = m_PubFuncs[func_id];
if (!pFunc) {
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)
{
return NULL;
}
if (FindPublicByName(public_name, &index) != SP_ERROR_NONE)
return NULL;
CFunction *pFunc = m_PubFuncs[index];
if (!pFunc)
{
sp_public_t *pub = NULL;
GetPublicByIndex(index, &pub);
if (pub)
{
m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
}
pFunc = m_PubFuncs[index];
}
CFunction *pFunc = m_PubFuncs[index];
if (!pFunc) {
sp_public_t *pub = NULL;
GetPublicByIndex(index, &pub);
if (pub)
m_PubFuncs[index] = new CFunction(this, (index << 1) | 1, index);
pFunc = m_PubFuncs[index];
}
return pFunc;
return pFunc;
}
bool BaseRuntime::IsDebugging()
{
return true;
return true;
}
void BaseRuntime::SetPauseState(bool paused)
{
if (paused)
{
m_pPlugin->run_flags |= SPFLAG_PLUGIN_PAUSED;
}
else
{
m_pPlugin->run_flags &= ~SPFLAG_PLUGIN_PAUSED;
}
if (paused)
{
m_plugin.run_flags |= SPFLAG_PLUGIN_PAUSED;
}
else
{
m_plugin.run_flags &= ~SPFLAG_PLUGIN_PAUSED;
}
}
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 mem = 0;
size_t mem = 0;
mem += sizeof(this);
mem += sizeof(sp_plugin_t);
mem += sizeof(BaseContext);
mem += m_pPlugin->base_size;
mem += sizeof(this);
mem += sizeof(sp_plugin_t);
mem += sizeof(BaseContext);
mem += m_plugin.base_size;
return mem;
return mem;
}
unsigned char *BaseRuntime::GetCodeHash()
{
return m_CodeHash;
return m_CodeHash;
}
unsigned char *BaseRuntime::GetDataHash()
{
return m_DataHash;
return m_DataHash;
}
BaseContext *BaseRuntime::GetBaseContext()
{
return m_pCtx;
return m_pCtx;
}
int BaseRuntime::ApplyCompilationOptions(ICompilation *co)
int
BaseRuntime::ApplyCompilationOptions(ICompilation *co)
{
if (co == NULL)
{
return SP_ERROR_NONE;
}
if (co == NULL)
return SP_ERROR_NONE;
m_pCo = g_Jit.ApplyOptions(m_pCo, co);
m_pPlugin->prof_flags = ((CompData *)m_pCo)->profile;
co_ = g_Jit.ApplyOptions(co_, co);
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)
{
return NULL;
}
/* Align to cell_t bytes */
heastk += sizeof(cell_t);
heastk -= heastk % sizeof(cell_t);
return m_FuncCache[idx - 1];
}
uint32_t BaseRuntime::AddJittedFunction(JitFunction *fn)
{
if (m_NumFuncs + 1 > m_MaxFuncs)
{
if (m_MaxFuncs == 0)
{
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;
m_plugin.mem_size = heastk;
m_plugin.memory = new uint8_t[heastk];
m_plugin.profiler = g_engine2.GetProfiler();
m_pCtx = new BaseContext(this);
co_ = 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_
#define _INCLUDE_SOURCEPAWN_JIT_RUNTIME_H_
#include <sp_vm_api.h>
#include <ke_vector.h>
#include "jit_shared.h"
#include "sp_vm_function.h"
@ -11,65 +13,93 @@ class JitFunction;
class DebugInfo : public IPluginDebugInfo
{
public:
DebugInfo(sp_plugin_t *plugin);
DebugInfo(sp_plugin_t *plugin);
public:
int LookupFile(ucell_t addr, const char **filename);
int LookupFunction(ucell_t addr, const char **name);
int LookupLine(ucell_t addr, uint32_t *line);
int LookupFile(ucell_t addr, const char **filename);
int LookupFunction(ucell_t addr, const char **name);
int LookupLine(ucell_t addr, uint32_t *line);
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 */
class BaseRuntime : public SourcePawn::IPluginRuntime
{
public:
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];
unsigned char m_DataHash[16];
public:
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 *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_

View File

@ -12,12 +12,12 @@ PROJECT = sourcepawn.jit.x86
OBJECTS = dll_exports.cpp \
x86/jit_x86.cpp \
x86/opcode_helpers.cpp \
sp_vm_basecontext.cpp \
sp_vm_engine.cpp \
sp_vm_function.cpp \
engine2.cpp \
BaseRuntime.cpp \
opcodes.cpp \
jit_function.cpp \
md5/md5.cpp \
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,76 +1,263 @@
/**
* 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 <sp_vm_api.h>
#include <stdlib.h>
#include "x86/jit_x86.h"
#include "dll_exports.h"
#include "sp_vm_engine.h"
#include "engine2.h"
SourcePawnEngine2 g_engine2;
EXPORTFUNC ISourcePawnEngine *GetSourcePawnEngine1()
{
return &g_engine1;
}
EXPORTFUNC ISourcePawnEngine2 *GetSourcePawnEngine2()
{
return &g_engine2;
}
#if defined __linux__ || defined __APPLE__
extern "C" void __cxa_pure_virtual(void)
{
}
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void *ptr)
{
free(ptr);
}
void operator delete[](void * ptr)
{
free(ptr);
}
#endif
/**
* vim: set ts=4 sts=4 sw=4 tw=99 noet:
* =============================================================================
* 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 <sp_vm_api.h>
#include <stdlib.h>
#include <stdarg.h>
#include "x86/jit_x86.h"
#include "dll_exports.h"
#include "sp_vm_engine.h"
#include "engine2.h"
using namespace SourcePawn;
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()
{
return &g_engine1;
}
EXPORTFUNC ISourcePawnEngine2 *GetSourcePawnEngine2()
{
return &g_engine2;
}
#endif
#if defined __linux__ || defined __APPLE__
extern "C" void __cxa_pure_virtual(void)
{
}
void *operator new(size_t size)
{
return malloc(size);
}
void *operator new[](size_t size)
{
return malloc(size);
}
void operator delete(void *ptr)
{
free(ptr);
}
void operator delete[](void * ptr)
{
free(ptr);
}
#endif

View File

@ -106,19 +106,19 @@ IPluginRuntime *SourcePawnEngine2::LoadPlugin(ICompilation *co, const char *file
#endif
)
{
pRuntime->m_pPlugin->name = strdup(&file[i+1]);
pRuntime->SetName(&file[i+1]);
break;
}
}
if (pRuntime->m_pPlugin->name == NULL)
if (!pRuntime->plugin()->name)
{
pRuntime->m_pPlugin->name = strdup(file);
pRuntime->SetName(file);
}
pRuntime->ApplyCompilationOptions(co);
fclose(fp);
fclose(fp);
return pRuntime;
@ -204,7 +204,7 @@ IPluginRuntime *SourcePawnEngine2::CreateEmptyRuntime(const char *name, uint32_t
return NULL;
}
rt->m_pPlugin->name = strdup(name != NULL ? name : "<anonymous>");
rt->SetName(name != NULL ? name : "<anonymous>");
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
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -46,7 +46,6 @@ using namespace SourcePawn;
BaseContext::BaseContext(BaseRuntime *pRuntime)
{
m_pRuntime = pRuntime;
m_pPlugin = m_pRuntime->m_pPlugin;
m_InExec = false;
m_CustomMsg = false;
@ -75,8 +74,8 @@ BaseContext::BaseContext(BaseRuntime *pRuntime)
m_pNullString = NULL;
}
m_ctx.hp = m_pPlugin->data_size;
m_ctx.sp = m_pPlugin->mem_size - sizeof(cell_t);
m_ctx.hp = m_pRuntime->plugin()->data_size;
m_ctx.sp = m_pRuntime->plugin()->mem_size - sizeof(cell_t);
m_ctx.frm = m_ctx.sp;
m_ctx.n_err = 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;
}
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 */
*addr = (cell_t)cells;
addr++;
@ -229,12 +228,12 @@ int BaseContext::HeapPop(cell_t local_addr)
/* check the bounds of this address */
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;
}
addr = (cell_t *)(m_pPlugin->memory + local_addr);
addr = (cell_t *)(m_pRuntime->plugin()->memory + local_addr);
cellcount = (*addr) * sizeof(cell_t);
/* check if this memory count looks valid */
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)
{
if (local_addr < (cell_t)m_pPlugin->data_size)
if (local_addr < (cell_t)m_pRuntime->plugin()->data_size)
{
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)
{
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;
}
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;
@ -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)
{
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;
}
*addr = (char *)(m_pPlugin->memory + local_addr);
*addr = (char *)(m_pRuntime->plugin()->memory + local_addr);
return SP_ERROR_NONE;
}
@ -385,7 +384,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
size_t len;
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;
}
@ -396,7 +395,7 @@ int BaseContext::StringToLocal(cell_t local_addr, size_t bytes, const char *sour
}
len = strlen(source);
dest = (char *)(m_pPlugin->memory + local_addr);
dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
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))
|| (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;
}
@ -466,7 +465,7 @@ int BaseContext::StringToLocalUTF8(cell_t local_addr, size_t maxbytes, const cha
}
len = strlen(source);
dest = (char *)(m_pPlugin->memory + local_addr);
dest = (char *)(m_pRuntime->plugin()->memory + local_addr);
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. */
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. */
if ((fn = m_pRuntime->m_PubJitFuncs[public_id]) == NULL)
{
uint32_t func_idx;
/* 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;
}
else
@ -629,7 +625,7 @@ int BaseContext::Execute2(IPluginFunction *function, const cell_t *params, unsig
/* Push parameters */
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;
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.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;
@ -746,7 +742,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
{
uint32_t max, iter;
sp_fdbg_symbol_t *sym;
sp_fdbg_arraydim_t *arr;
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
max = m_pPlugin->debug.syms_num;
@ -765,7 +760,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
if (sym->dimcount > 0)
{
cursor += sizeof(sp_fdbg_symbol_t);
arr = (sp_fdbg_arraydim_t *)cursor;
cursor += sizeof(sp_fdbg_arraydim_t) * sym->dimcount;
continue;
}
@ -779,7 +773,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
{
uint32_t max, iter;
sp_u_fdbg_symbol_t *sym;
sp_u_fdbg_arraydim_t *arr;
uint8_t *cursor = (uint8_t *)(m_pPlugin->debug.symbols);
max = m_pPlugin->debug.syms_num;
@ -798,7 +791,6 @@ int DebugInfo::LookupFunction(ucell_t addr, const char **name)
if (sym->dimcount > 0)
{
cursor += sizeof(sp_u_fdbg_symbol_t);
arr = (sp_u_fdbg_arraydim_t *)cursor;
cursor += sizeof(sp_u_fdbg_arraydim_t) * sym->dimcount;
continue;
}
@ -848,7 +840,7 @@ int BaseContext::GetLastNativeError()
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)

View File

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

View File

@ -58,7 +58,7 @@ SourcePawnEngine g_engine1;
using namespace SourcePawn;
#define ERROR_MESSAGE_MAX 25
#define ERROR_MESSAGE_MAX 29
static const char *g_ErrorMsgTable[] =
{
NULL,
@ -89,6 +89,8 @@ static const char *g_ErrorMsgTable[] =
"Call was aborted",
"Plugin format is too old",
"Plugin format is too new",
"Out of memory",
"Integer overflow"
};
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
* Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
@ -35,164 +35,180 @@
#include <sp_vm_types.h>
#include <sp_vm_api.h>
#include <KeCodeAllocator.h>
#include "jit_helpers.h"
#include <assembler-x86.h>
#include <ke_vector.h>
#include "jit_shared.h"
#include "BaseRuntime.h"
#include "jit_function.h"
#include "opcodes.h"
using namespace SourcePawn;
#define JIT_INLINE_ERRORCHECKS (1<<0)
#define JIT_INLINE_NATIVES (1<<1)
#define STACK_MARGIN 64 //8 parameters of safety, I guess
#define JIT_FUNCMAGIC 0x214D4148 //magic function offset
#define JIT_INLINE_ERRORCHECKS (1<<0)
#define JIT_INLINE_NATIVES (1<<1)
#define STACK_MARGIN 64 //8 parameters of safety, I guess
#define JIT_FUNCMAGIC 0x214D4148 //magic function offset
#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_PROFILER 2 //profiler
#define JITVARS_PLUGIN 3 //sp_plugin_t
#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_PROFILER 2 //profiler
#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])
typedef struct tracker_s
{
size_t size;
ucell_t *pBase;
ucell_t *pCur;
size_t size;
ucell_t *pBase;
ucell_t *pCur;
} tracker_t;
typedef struct funcinfo_s
{
unsigned int magic;
unsigned int index;
unsigned int magic;
unsigned int index;
} funcinfo_t;
typedef struct functracker_s
{
unsigned int num_functions;
unsigned int code_size;
unsigned int num_functions;
unsigned int code_size;
} functracker_t;
struct floattbl_t
struct CallThunk
{
floattbl_t()
{
found = false;
index = 0;
}
bool found;
unsigned int index;
};
Label call;
cell_t pcode_offset;
struct call_thunk_t
{
jitoffs_t patch_addr;
cell_t pcode_offs;
jitoffs_t thunk_addr;
CallThunk(cell_t pcode_offset)
: pcode_offset(pcode_offset)
{
}
};
class CompData : public ICompilation
{
public:
CompData()
: runtime(NULL),
plugin(NULL),
rebase(NULL),
jit_float_table(NULL),
profile(0),
inline_level(0),
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();
CompData()
: profile(0),
inline_level(0)
{
};
bool SetOption(const char *key, const char *val);
void Abort();
public:
BaseRuntime *runtime; /* runtime handle */
sp_plugin_t *plugin; /* plugin handle */
uint8_t *rebase; /* relocation map */
floattbl_t *jit_float_table;
cell_t cur_func; /* current func pcode offset */
/* Options */
int profile; /* profiling flags */
int inline_level; /* inline optimization level */
/* Per-compilation properties */
int error_set; /* error code to halt process */
unsigned int func_idx; /* current function index */
jitoffs_t jit_error_bounds;
jitoffs_t jit_error_divzero;
jitoffs_t jit_error_stacklow;
jitoffs_t jit_error_stackmin;
jitoffs_t jit_error_memaccess;
jitoffs_t jit_error_heaplow;
jitoffs_t jit_error_heapmin;
jitoffs_t jit_extern_error; /* returning generic error */
jitoffs_t jit_sysreq_c; /* old version! */
uint32_t num_thunks; /* number of thunks needed */
uint32_t max_thunks; /* maximum number of thunks */
call_thunk_t *thunks; /* thunk array */
cell_t cur_func; /* current func pcode offset */
/* Options */
int profile; /* profiling flags */
int inline_level; /* inline optimization level */
/* Per-compilation properties */
unsigned int func_idx; /* current function index */
};
class Compiler
{
public:
Compiler(BaseRuntime *rt, cell_t pcode_offs);
~Compiler();
JitFunction *emit(int *errp);
private:
bool setup(cell_t pcode_offs);
bool emitOp(OPCODE op);
cell_t readCell();
private:
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
{
public:
JITX86();
public:
bool InitializeJIT();
void ShutdownJIT();
ICompilation *StartCompilation(BaseRuntime *runtime);
ICompilation *StartCompilation();
void SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx);
void FreeContextVars(sp_context_t *ctx);
SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData);
void DestroyFakeNative(SPVM_NATIVE_FUNC func);
JitFunction *CompileFunction(BaseRuntime *runtime, cell_t pcode_offs, int *err);
ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT);
int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result);
public:
void *GetGenArrayIntrinsic();
void *GetReturnPoint();
void *GetRoundingTable();
void *AllocCode(size_t size);
void FreeCode(void *code);
private:
void *m_pJitEntry; /* Entry function */
void *m_pJitReturn; /* Return point for errors */
int m_RoundTable[3]; /* [-1, 0, 1] rounding table */
void *m_pJitGenArray; /* Generates an array */
public:
JITX86();
public:
bool InitializeJIT();
void ShutdownJIT();
ICompilation *StartCompilation(BaseRuntime *runtime);
ICompilation *StartCompilation();
void SetupContextVars(BaseRuntime *runtime, BaseContext *pCtx, sp_context_t *ctx);
void FreeContextVars(sp_context_t *ctx);
SPVM_NATIVE_FUNC CreateFakeNative(SPVM_FAKENATIVE_FUNC callback, void *pData);
void DestroyFakeNative(SPVM_NATIVE_FUNC func);
JitFunction *CompileFunction(BaseRuntime *runtime, cell_t pcode_offs, int *err);
ICompilation *ApplyOptions(ICompilation *_IN, ICompilation *_OUT);
int InvokeFunction(BaseRuntime *runtime, JitFunction *fn, cell_t *result);
public:
ExternalAddress GetGenArrayIntrinsic() {
return ExternalAddress(m_pJitGenArray);
}
ExternalAddress GetUniversalReturn() {
return ExternalAddress(m_pJitReturn);
}
void *AllocCode(size_t size);
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);
cell_t NativeCallback_Profile(sp_context_t *ctx, ucell_t native_idx, cell_t *params);
uint32_t FuncLookup(CompData *data, cell_t pcode_offs);
jitoffs_t RelocLookup(JitWriter *jit, cell_t pcode_offs, bool relative=false);
void *CompileThunk(BaseRuntime *runtime, cell_t pcode_ffs, void *jmploc_addr);
const Register pri = eax;
const Register alt = edx;
const Register stk = edi;
const Register dat = ebp;
const Register tmp = ecx;
const Register info = esi;
const Register frm = ebx;
#define AMX_REG_PRI REG_EAX
#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_NUM_INFO_VARS 9
#define AMX_INFO_FRM AMX_REG_INFO //not relocated
#define AMX_INFO_FRAME 0 //(same thing as above)
#define AMX_INFO_HEAP 4 //not relocated
#define AMX_INFO_RETVAL 8 //physical
#define AMX_INFO_CONTEXT 12 //physical
#define AMX_INFO_STACKTOP 16 //relocated
#define AMX_INFO_CIP 20 //pcode CIP
#define AMX_INFO_DATASIZE 24 //plugin->data_size
#define AMX_INFO_MEMORY 28 //plugin->memory
#define AMX_INFO_NSTACK 32 //native stack
#define AMX_INFO_FRAME 0 //(same thing as above)
#define AMX_INFO_HEAP 4 //not relocated
#define AMX_INFO_RETVAL 8 //physical
#define AMX_INFO_CONTEXT 12 //physical
#define AMX_INFO_STACKTOP 16 //relocated
#define AMX_INFO_CIP 20 //pcode CIP
#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 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_