summaryrefslogtreecommitdiff
path: root/src/qcommon/vm_x86_64.c
diff options
context:
space:
mode:
authorTim Angus <tim@ngus.net>2007-08-24 00:32:53 +0000
committerTim Angus <tim@ngus.net>2007-08-24 00:32:53 +0000
commitd6cbf3366e63e4e4333e49377ee994d26b266a6c (patch)
tree18c6488dbd654cb747534fc7a1dcd240be492bdb /src/qcommon/vm_x86_64.c
parent9bb5d25cc24fd678660af34b9b19e21a37f09bd3 (diff)
* Merged ioq3-r1133
+ PNG image loader + Non-gas dependent x86_64 VM + Collision optimisations + Slew of other bug fixes
Diffstat (limited to 'src/qcommon/vm_x86_64.c')
-rw-r--r--src/qcommon/vm_x86_64.c152
1 files changed, 134 insertions, 18 deletions
diff --git a/src/qcommon/vm_x86_64.c b/src/qcommon/vm_x86_64.c
index e8e827e5..814acfef 100644
--- a/src/qcommon/vm_x86_64.c
+++ b/src/qcommon/vm_x86_64.c
@@ -29,9 +29,15 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
+#include <sys/time.h>
+#include <time.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
+#include <stdarg.h>
+
+//#define USE_GAS
+//#define DEBUG_VM
#ifdef DEBUG_VM
#define Dfprintf(fd, args...) fprintf(fd, ##args)
@@ -40,6 +46,19 @@ static FILE* qdasmout;
#define Dfprintf(args...)
#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);
/*
@@ -207,8 +226,29 @@ static unsigned char op_argsize[256] =
[OP_BLOCK_COPY] = 4,
};
+#ifdef USE_GAS
#define emit(x...) \
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
#define IJ(op) \
@@ -216,7 +256,8 @@ static unsigned char op_argsize[256] =
emit("movl 4(%%rsi), %%eax"); \
emit("cmpl 8(%%rsi), %%eax"); \
emit(op " i_%08x", instruction+1); \
- emit("jmp i_%08x", iarg);
+ JMPIARG \
+ neednilabel = 1;
#ifdef USE_X87
#define FJ(bits, op) \
@@ -226,7 +267,8 @@ static unsigned char op_argsize[256] =
emit("fnstsw %%ax");\
emit("testb $" #bits ", %%ah");\
emit(op " i_%08x", instruction+1);\
- emit("jmp i_%08x", iarg);
+ JMPIARG \
+ neednilabel = 1;
#define XJ(x)
#else
#define FJ(x, y)
@@ -236,7 +278,8 @@ static unsigned char op_argsize[256] =
emit("ucomiss 8(%%rsi), %%xmm0");\
emit("jp i_%08x", instruction+1);\
emit(op " i_%08x", instruction+1);\
- emit("jmp i_%08x", iarg);
+ JMPIARG \
+ neednilabel = 1;
#endif
#define SIMPLE(op) \
@@ -293,9 +336,14 @@ static unsigned char op_argsize[256] =
static void* getentrypoint(vm_t* vm)
{
+#ifdef USE_GAS
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)
{
int fd = -1;
@@ -383,6 +431,7 @@ static int doas(char* in, char* out, unsigned char** compiledcode)
return size;
}
+#endif // USE_GAS
static void block_copy_vm(unsigned dest, unsigned src, unsigned count)
{
@@ -411,8 +460,13 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
char* code;
unsigned iarg = 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_o[2*MAX_QPATH]; // file written by as
#ifdef DEBUG_VM
@@ -420,16 +474,16 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
#endif
FILE* fh_s;
int fd_s, fd_o;
- byte* compiledcode;
- int compiledsize;
+
+ gettimeofday(&tvstart, NULL);
Com_Printf("compiling %s\n", vm->name);
#ifdef DEBUG_VM
snprintf(fn_s, sizeof(fn_s), "%.63s.s", vm->name);
snprintf(fn_o, sizeof(fn_o), "%.63s.o", vm->name);
- fd_s = open(fn_s, O_CREAT|O_WRONLY, 0644);
- fd_o = open(fn_o, 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|O_TRUNC, 0644);
#else
snprintf(fn_s, sizeof(fn_s), "/tmp/%.63s.s_XXXXXX", vm->name);
snprintf(fn_o, sizeof(fn_o), "/tmp/%.63s.o_XXXXXX", vm->name);
@@ -463,25 +517,50 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
return;
}
- // translate all instructions
- pc = 0;
- code = (char *)header + header->codeOffset;
-
emit("start:");
emit("or %%r8, %%r8"); // check whether to set up instruction pointers
emit("jnz main");
emit("jmp setupinstructionpointers");
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 )
{
op = code[ 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("movq $%d, %%r15", instruction);
emit("nop");
@@ -502,7 +581,17 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
{
Dfprintf(qdasmout, "%s\n", opnames[op]);
}
+
+#ifdef USE_GAS
emit("i_%08x:", instruction);
+#else
+ if(neednilabel)
+ {
+ emit("i_%08x:", instruction);
+ neednilabel = 0;
+ }
+#endif
+
switch ( op )
{
case OP_UNDEF:
@@ -561,6 +650,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
// emit("frstor 4(%%rsi)");
emit("addq $4, %%rsi");
emit("movl %%eax, (%%rsi)"); // store return value
+ neednilabel = 1;
break;
case OP_PUSH:
emit("addq $4, %%rsi");
@@ -629,7 +719,8 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
emit("jp dojump_i_%08x", instruction);
emit("jz i_%08x", instruction+1);
emit("dojump_i_%08x:", instruction);
- emit("jmp i_%08x", iarg);
+ JMPIARG
+ neednilabel = 1;
#endif
break;
case OP_LTF:
@@ -856,7 +947,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
}
}
-
+#ifdef USE_GAS
emit("setupinstructionpointers:");
emit("movq $%lu, %%rax", (unsigned long)vm->instructionPointers);
for ( instruction = 0; instruction < header->instructionCount; ++instruction )
@@ -889,8 +980,17 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
vm->codeBase = compiledcode; // remember to skip ELF header!
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;
+#ifdef USE_GAS
entryPoint = getentrypoint(vm);
// __asm__ __volatile__ ("int3");
@@ -911,8 +1011,6 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) {
fclose(qdasmout);
#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:
close(fd_o);
@@ -923,12 +1021,30 @@ out:
unlink(fn_s);
}
#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)
{
+#ifdef USE_GAS
munmap(self->codeBase, self->codeLength);
+#elif _WIN32
+ VirtualFree(self->codeBase, self->codeLength, MEM_RELEASE);
+#else
+ munmap(self->codeBase, self->codeLength);
+#endif
}
/*