new x86_64 vm that doesn't use gas

This commit is contained in:
Ludwig Nussel 2007-08-16 11:02:21 +00:00
parent 64239037e0
commit fbe65853e4
3 changed files with 1555 additions and 20 deletions

View File

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

View File

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

File diff suppressed because it is too large Load Diff