mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2025-02-07 08:21:48 +00:00
new x86_64 vm that doesn't use gas
This commit is contained in:
parent
64239037e0
commit
fbe65853e4
3 changed files with 1555 additions and 20 deletions
4
Makefile
4
Makefile
|
@ -1059,7 +1059,7 @@ ifeq ($(HAVE_VM_COMPILED),true)
|
||||||
Q3OBJ += $(B)/client/vm_x86.o
|
Q3OBJ += $(B)/client/vm_x86.o
|
||||||
endif
|
endif
|
||||||
ifeq ($(ARCH),x86_64)
|
ifeq ($(ARCH),x86_64)
|
||||||
Q3OBJ += $(B)/client/vm_x86_64.o
|
Q3OBJ += $(B)/client/vm_x86_64.o $(B)/client/vm_x86_64_assembler.o
|
||||||
endif
|
endif
|
||||||
ifeq ($(ARCH),ppc)
|
ifeq ($(ARCH),ppc)
|
||||||
Q3OBJ += $(B)/client/vm_ppc.o
|
Q3OBJ += $(B)/client/vm_ppc.o
|
||||||
|
@ -1223,7 +1223,7 @@ ifeq ($(HAVE_VM_COMPILED),true)
|
||||||
Q3DOBJ += $(B)/ded/vm_x86.o
|
Q3DOBJ += $(B)/ded/vm_x86.o
|
||||||
endif
|
endif
|
||||||
ifeq ($(ARCH),x86_64)
|
ifeq ($(ARCH),x86_64)
|
||||||
Q3DOBJ += $(B)/ded/vm_x86_64.o
|
Q3DOBJ += $(B)/ded/vm_x86_64.o $(B)/client/vm_x86_64_assembler.o
|
||||||
endif
|
endif
|
||||||
ifeq ($(ARCH),ppc)
|
ifeq ($(ARCH),ppc)
|
||||||
Q3DOBJ += $(B)/ded/vm_ppc.o
|
Q3DOBJ += $(B)/ded/vm_ppc.o
|
||||||
|
|
|
@ -28,9 +28,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <time.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
//#define USE_GAS
|
||||||
|
//#define DEBUG_VM
|
||||||
|
|
||||||
#ifdef DEBUG_VM
|
#ifdef DEBUG_VM
|
||||||
#define Dfprintf(fd, args...) fprintf(fd, ##args)
|
#define Dfprintf(fd, args...) fprintf(fd, ##args)
|
||||||
|
@ -39,6 +45,19 @@ static FILE* qdasmout;
|
||||||
#define Dfprintf(args...)
|
#define Dfprintf(args...)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define VM_X86_64_MMAP
|
||||||
|
|
||||||
|
#ifndef USE_GAS
|
||||||
|
void assembler_set_output(char* buf);
|
||||||
|
size_t assembler_get_code_size(void);
|
||||||
|
void assembler_init(int pass);
|
||||||
|
void assemble_line(const char* input, size_t len);
|
||||||
|
#ifdef Dfprintf
|
||||||
|
#undef Dfprintf
|
||||||
|
#define Dfprintf(args...)
|
||||||
|
#endif
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
static void VM_Destroy_Compiled(vm_t* self);
|
static void VM_Destroy_Compiled(vm_t* self);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -206,8 +225,29 @@ static unsigned char op_argsize[256] =
|
||||||
[OP_BLOCK_COPY] = 4,
|
[OP_BLOCK_COPY] = 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
#define emit(x...) \
|
#define emit(x...) \
|
||||||
do { fprintf(fh_s, ##x); fputc('\n', fh_s); } while(0)
|
do { fprintf(fh_s, ##x); fputc('\n', fh_s); } while(0)
|
||||||
|
#else
|
||||||
|
void emit(const char* fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
char line[4096];
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vsnprintf(line, sizeof(line), fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
assemble_line(line, strlen(line));
|
||||||
|
}
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
|
#define JMPIARG \
|
||||||
|
emit("jmp i_%08x", iarg);
|
||||||
|
#else
|
||||||
|
#define JMPIARG \
|
||||||
|
emit("movq $%lu, %%rax", vm->codeBase+vm->instructionPointers[iarg]); \
|
||||||
|
emit("jmpq *%rax");
|
||||||
|
#endif
|
||||||
|
|
||||||
// integer compare and jump
|
// integer compare and jump
|
||||||
#define IJ(op) \
|
#define IJ(op) \
|
||||||
|
@ -215,7 +255,8 @@ static unsigned char op_argsize[256] =
|
||||||
emit("movl 4(%%rsi), %%eax"); \
|
emit("movl 4(%%rsi), %%eax"); \
|
||||||
emit("cmpl 8(%%rsi), %%eax"); \
|
emit("cmpl 8(%%rsi), %%eax"); \
|
||||||
emit(op " i_%08x", instruction+1); \
|
emit(op " i_%08x", instruction+1); \
|
||||||
emit("jmp i_%08x", iarg);
|
JMPIARG \
|
||||||
|
neednilabel = 1;
|
||||||
|
|
||||||
#ifdef USE_X87
|
#ifdef USE_X87
|
||||||
#define FJ(bits, op) \
|
#define FJ(bits, op) \
|
||||||
|
@ -225,7 +266,8 @@ static unsigned char op_argsize[256] =
|
||||||
emit("fnstsw %%ax");\
|
emit("fnstsw %%ax");\
|
||||||
emit("testb $" #bits ", %%ah");\
|
emit("testb $" #bits ", %%ah");\
|
||||||
emit(op " i_%08x", instruction+1);\
|
emit(op " i_%08x", instruction+1);\
|
||||||
emit("jmp i_%08x", iarg);
|
JMPIARG \
|
||||||
|
neednilabel = 1;
|
||||||
#define XJ(x)
|
#define XJ(x)
|
||||||
#else
|
#else
|
||||||
#define FJ(x, y)
|
#define FJ(x, y)
|
||||||
|
@ -235,7 +277,8 @@ static unsigned char op_argsize[256] =
|
||||||
emit("ucomiss 8(%%rsi), %%xmm0");\
|
emit("ucomiss 8(%%rsi), %%xmm0");\
|
||||||
emit("jp i_%08x", instruction+1);\
|
emit("jp i_%08x", instruction+1);\
|
||||||
emit(op " i_%08x", instruction+1);\
|
emit(op " i_%08x", instruction+1);\
|
||||||
emit("jmp i_%08x", iarg);
|
JMPIARG \
|
||||||
|
neednilabel = 1;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define SIMPLE(op) \
|
#define SIMPLE(op) \
|
||||||
|
@ -292,9 +335,14 @@ static unsigned char op_argsize[256] =
|
||||||
|
|
||||||
static void* getentrypoint(vm_t* vm)
|
static void* getentrypoint(vm_t* vm)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_GAS
|
||||||
return vm->codeBase+64; // skip ELF header
|
return vm->codeBase+64; // skip ELF header
|
||||||
|
#else
|
||||||
|
return vm->codeBase;
|
||||||
|
#endif // USE_GAS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
char* mmapfile(const char* fn, size_t* size)
|
char* mmapfile(const char* fn, size_t* size)
|
||||||
{
|
{
|
||||||
int fd = -1;
|
int fd = -1;
|
||||||
|
@ -382,6 +430,7 @@ static int doas(char* in, char* out, unsigned char** compiledcode)
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
static void block_copy_vm(unsigned dest, unsigned src, unsigned count)
|
static void block_copy_vm(unsigned dest, unsigned src, unsigned count)
|
||||||
{
|
{
|
||||||
|
@ -410,8 +459,13 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
char* code;
|
char* code;
|
||||||
unsigned iarg = 0;
|
unsigned iarg = 0;
|
||||||
unsigned char barg = 0;
|
unsigned char barg = 0;
|
||||||
void* entryPoint;
|
int neednilabel = 0;
|
||||||
|
struct timeval tvstart = {0, 0};
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
|
byte* compiledcode;
|
||||||
|
int compiledsize;
|
||||||
|
void* entryPoint;
|
||||||
char fn_s[2*MAX_QPATH]; // output file for assembler code
|
char fn_s[2*MAX_QPATH]; // output file for assembler code
|
||||||
char fn_o[2*MAX_QPATH]; // file written by as
|
char fn_o[2*MAX_QPATH]; // file written by as
|
||||||
#ifdef DEBUG_VM
|
#ifdef DEBUG_VM
|
||||||
|
@ -419,16 +473,16 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
#endif
|
#endif
|
||||||
FILE* fh_s;
|
FILE* fh_s;
|
||||||
int fd_s, fd_o;
|
int fd_s, fd_o;
|
||||||
byte* compiledcode;
|
|
||||||
int compiledsize;
|
gettimeofday(&tvstart, NULL);
|
||||||
|
|
||||||
Com_Printf("compiling %s\n", vm->name);
|
Com_Printf("compiling %s\n", vm->name);
|
||||||
|
|
||||||
#ifdef DEBUG_VM
|
#ifdef DEBUG_VM
|
||||||
snprintf(fn_s, sizeof(fn_s), "%.63s.s", vm->name);
|
snprintf(fn_s, sizeof(fn_s), "%.63s.s", vm->name);
|
||||||
snprintf(fn_o, sizeof(fn_o), "%.63s.o", vm->name);
|
snprintf(fn_o, sizeof(fn_o), "%.63s.o", vm->name);
|
||||||
fd_s = open(fn_s, O_CREAT|O_WRONLY, 0644);
|
fd_s = open(fn_s, O_CREAT|O_WRONLY|O_TRUNC, 0644);
|
||||||
fd_o = open(fn_o, O_CREAT|O_WRONLY, 0644);
|
fd_o = open(fn_o, O_CREAT|O_WRONLY|O_TRUNC, 0644);
|
||||||
#else
|
#else
|
||||||
snprintf(fn_s, sizeof(fn_s), "/tmp/%.63s.s_XXXXXX", vm->name);
|
snprintf(fn_s, sizeof(fn_s), "/tmp/%.63s.s_XXXXXX", vm->name);
|
||||||
snprintf(fn_o, sizeof(fn_o), "/tmp/%.63s.o_XXXXXX", vm->name);
|
snprintf(fn_o, sizeof(fn_o), "/tmp/%.63s.o_XXXXXX", vm->name);
|
||||||
|
@ -462,25 +516,50 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate all instructions
|
|
||||||
pc = 0;
|
|
||||||
code = (char *)header + header->codeOffset;
|
|
||||||
|
|
||||||
emit("start:");
|
emit("start:");
|
||||||
emit("or %%r8, %%r8"); // check whether to set up instruction pointers
|
emit("or %%r8, %%r8"); // check whether to set up instruction pointers
|
||||||
emit("jnz main");
|
emit("jnz main");
|
||||||
emit("jmp setupinstructionpointers");
|
emit("jmp setupinstructionpointers");
|
||||||
|
|
||||||
emit("main:");
|
emit("main:");
|
||||||
|
#else // USE_GAS
|
||||||
|
int pass;
|
||||||
|
size_t compiledOfs = 0;
|
||||||
|
|
||||||
|
gettimeofday(&tvstart, NULL);
|
||||||
|
|
||||||
|
for (pass = 0; pass < 2; ++pass) {
|
||||||
|
|
||||||
|
if(pass)
|
||||||
|
{
|
||||||
|
compiledOfs = assembler_get_code_size();
|
||||||
|
vm->codeLength = compiledOfs;
|
||||||
|
vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
|
||||||
|
if(vm->codeBase == (void*)-1)
|
||||||
|
Com_Error(ERR_DROP, "VM_CompileX86: can't mmap memory");
|
||||||
|
|
||||||
|
assembler_set_output((char*)vm->codeBase);
|
||||||
|
}
|
||||||
|
|
||||||
|
assembler_init(pass);
|
||||||
|
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
|
// translate all instructions
|
||||||
|
pc = 0;
|
||||||
|
code = (char *)header + header->codeOffset;
|
||||||
|
|
||||||
for ( instruction = 0; instruction < header->instructionCount; ++instruction )
|
for ( instruction = 0; instruction < header->instructionCount; ++instruction )
|
||||||
{
|
{
|
||||||
op = code[ pc ];
|
op = code[ pc ];
|
||||||
++pc;
|
++pc;
|
||||||
|
|
||||||
vm->instructionPointers[instruction] = pc;
|
#ifndef USE_GAS
|
||||||
|
vm->instructionPointers[instruction] = assembler_get_code_size();
|
||||||
|
#endif
|
||||||
|
|
||||||
#if 0
|
/* store current instruction number in r15 for debugging */
|
||||||
|
#if 1
|
||||||
emit("nop");
|
emit("nop");
|
||||||
emit("movq $%d, %%r15", instruction);
|
emit("movq $%d, %%r15", instruction);
|
||||||
emit("nop");
|
emit("nop");
|
||||||
|
@ -501,7 +580,17 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
{
|
{
|
||||||
Dfprintf(qdasmout, "%s\n", opnames[op]);
|
Dfprintf(qdasmout, "%s\n", opnames[op]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
emit("i_%08x:", instruction);
|
emit("i_%08x:", instruction);
|
||||||
|
#else
|
||||||
|
if(neednilabel)
|
||||||
|
{
|
||||||
|
emit("i_%08x:", instruction);
|
||||||
|
neednilabel = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
switch ( op )
|
switch ( op )
|
||||||
{
|
{
|
||||||
case OP_UNDEF:
|
case OP_UNDEF:
|
||||||
|
@ -560,6 +649,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
// emit("frstor 4(%%rsi)");
|
// emit("frstor 4(%%rsi)");
|
||||||
emit("addq $4, %%rsi");
|
emit("addq $4, %%rsi");
|
||||||
emit("movl %%eax, (%%rsi)"); // store return value
|
emit("movl %%eax, (%%rsi)"); // store return value
|
||||||
|
neednilabel = 1;
|
||||||
break;
|
break;
|
||||||
case OP_PUSH:
|
case OP_PUSH:
|
||||||
emit("addq $4, %%rsi");
|
emit("addq $4, %%rsi");
|
||||||
|
@ -628,7 +718,8 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
emit("jp dojump_i_%08x", instruction);
|
emit("jp dojump_i_%08x", instruction);
|
||||||
emit("jz i_%08x", instruction+1);
|
emit("jz i_%08x", instruction+1);
|
||||||
emit("dojump_i_%08x:", instruction);
|
emit("dojump_i_%08x:", instruction);
|
||||||
emit("jmp i_%08x", iarg);
|
JMPIARG
|
||||||
|
neednilabel = 1;
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case OP_LTF:
|
case OP_LTF:
|
||||||
|
@ -855,7 +946,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
emit("setupinstructionpointers:");
|
emit("setupinstructionpointers:");
|
||||||
emit("movq $%lu, %%rax", (unsigned long)vm->instructionPointers);
|
emit("movq $%lu, %%rax", (unsigned long)vm->instructionPointers);
|
||||||
for ( instruction = 0; instruction < header->instructionCount; ++instruction )
|
for ( instruction = 0; instruction < header->instructionCount; ++instruction )
|
||||||
|
@ -888,8 +979,17 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
vm->codeBase = compiledcode; // remember to skip ELF header!
|
vm->codeBase = compiledcode; // remember to skip ELF header!
|
||||||
vm->codeLength = compiledsize;
|
vm->codeLength = compiledsize;
|
||||||
|
|
||||||
|
#else // USE_GAS
|
||||||
|
}
|
||||||
|
assembler_init(0);
|
||||||
|
|
||||||
|
if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
|
||||||
|
Com_Error(ERR_DROP, "VM_CompileX86: mprotect failed");
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
vm->destroy = VM_Destroy_Compiled;
|
vm->destroy = VM_Destroy_Compiled;
|
||||||
|
|
||||||
|
#ifdef USE_GAS
|
||||||
entryPoint = getentrypoint(vm);
|
entryPoint = getentrypoint(vm);
|
||||||
|
|
||||||
// __asm__ __volatile__ ("int3");
|
// __asm__ __volatile__ ("int3");
|
||||||
|
@ -910,8 +1010,6 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
||||||
fclose(qdasmout);
|
fclose(qdasmout);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n", vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength );
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
close(fd_o);
|
close(fd_o);
|
||||||
|
|
||||||
|
@ -922,12 +1020,30 @@ out:
|
||||||
unlink(fn_s);
|
unlink(fn_s);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
#endif // USE_GAS
|
||||||
|
|
||||||
|
if(vm->compiled)
|
||||||
|
{
|
||||||
|
struct timeval tvdone = {0, 0};
|
||||||
|
struct timeval dur = {0, 0};
|
||||||
|
Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n", vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength );
|
||||||
|
|
||||||
|
gettimeofday(&tvdone, NULL);
|
||||||
|
timersub(&tvdone, &tvstart, &dur);
|
||||||
|
Com_Printf( "compilation took %lu.%06lu seconds\n", dur.tv_sec, dur.tv_usec );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void VM_Destroy_Compiled(vm_t* self)
|
void VM_Destroy_Compiled(vm_t* self)
|
||||||
{
|
{
|
||||||
|
#ifdef USE_GAS
|
||||||
munmap(self->codeBase, self->codeLength);
|
munmap(self->codeBase, self->codeLength);
|
||||||
|
#elif _WIN32
|
||||||
|
VirtualFree(self->codeBase, self->codeLength, MEM_RELEASE);
|
||||||
|
#else
|
||||||
|
munmap(self->codeBase, self->codeLength);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
1419
code/qcommon/vm_x86_64_assembler.c
Normal file
1419
code/qcommon/vm_x86_64_assembler.c
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue