diff options
Diffstat (limited to 'src/qcommon')
-rw-r--r-- | src/qcommon/vm_x86.c | 386 |
1 files changed, 66 insertions, 320 deletions
diff --git a/src/qcommon/vm_x86.c b/src/qcommon/vm_x86.c index ec43b631..afa29d4c 100644 --- a/src/qcommon/vm_x86.c +++ b/src/qcommon/vm_x86.c @@ -57,11 +57,12 @@ static void VM_Destroy_Compiled(vm_t* self); #define VMFREE_BUFFERS() do {Z_Free(buf); Z_Free(jused);} while(0) static byte *buf = NULL; static byte *jused = NULL; -static int jusedSize = 0; static int compiledOfs = 0; static byte *code = NULL; static int pc = 0; +static int *instructionPointers = NULL; + #define FTOL_PTR #ifdef _MSC_VER @@ -91,6 +92,8 @@ void AsmCall(void); static void (*const asmCallPtr)(void) = AsmCall; +static int callMask = 0; + static int instruction, pass; static int lastConst = 0; static int oc0, oc1, pop0, pop1; @@ -105,14 +108,6 @@ typedef enum static ELastCommand LastCommand; -static void ErrJump(void) -{ - Com_Error(ERR_DROP, "program tried to execute code outside VM"); - exit(1); -} - -static void (*const errJumpPtr)(void) = ErrJump; - /* ================= AsmCall @@ -130,21 +125,13 @@ __asm { sub edi, 4 test eax,eax jl systemCall - cmp eax, [currentVM->instructionCount] - jae badAddr // calling another vm function shl eax,2 - add eax, dword ptr [currentVM->instructionPointers] + add eax, dword ptr [instructionPointers] call dword ptr [eax] mov eax, dword ptr [edi] + and eax, [callMask] ret -badAddr: - call ErrJump - // leave something on the opstack - //add edi, 4 - //mov dword ptr [edi], 0 - //ret - systemCall: // convert negative num to system call number @@ -215,61 +202,44 @@ static void __attribute__((cdecl, used)) CallAsmCall(int const syscallNum, currentVM = vm; } -void AsmCall(void) -{ -__asm__ __volatile__( -// ".text\n\t" -// ".p2align 4,,15\n\t" -// #if defined __ELF__ -// ".type " CMANGFUNC(AsmCall) ", @function\n" -// #endif -// CMANGFUNC(AsmCall) ":\n\t" - "movl (%%edi), %%eax\n\t" - "subl $4, %%edi\n\t" -// "movl " CMANGVAR(currentVM) ", %ebx\n\t" - "testl %%eax, %%eax\n\t" +__asm__( + ".text\n\t" + ".p2align 4,,15\n\t" +#if defined __ELF__ + ".type " CMANGFUNC(AsmCall) ", @function\n" +#endif + CMANGFUNC(AsmCall) ":\n\t" + "movl (%edi), %eax\n\t" + "subl $4, %edi\n\t" + "testl %eax, %eax\n\t" "jl 0f\n\t" - "cmpl %0, %%eax\n\t" -// "cmpl 0x68(%ebx), %eax\n\t" - "jae 1f\n\t" - "shll $2, %%eax\n\t" - "addl %1, %%eax\n\t" -// "addl 0x64(%ebx), %eax\n\t" - "call *(%%eax)\n\t" - "movl (%%edi), %%eax\n\t" - "jmp 2f\n\t" - "1:\n\t" // bad address, leave something on the opstack - "call " CMANGFUNC(ErrJump) "\n\t" - //"addl $4, %edi\n\t" - //"movl $0, (%edi)\n\t" - //"ret\n\t" + "shll $2, %eax\n\t" + "addl " CMANGVAR(instructionPointers) ", %eax\n\t" + "call *(%eax)\n\t" + "movl (%edi), %eax\n\t" + "andl " CMANGVAR(callMask) ", %eax\n\t" + "ret\n" "0:\n\t" // system call - "notl %%eax\n\t" - "pushl %%ebp\n\t" - "movl %%esp, %%ebp\n\t" - - "andl $-16, %%esp\n\t" // align the stack so engine can use sse - "addl $4, %%esp\n\t" - "pushl %%edi\n\t" // opStack - "pushl %%esi\n\t" // programStack - "pushl %%eax\n\t" // syscallNum + "notl %eax\n\t" + "pushl %ebp\n\t" + "movl %esp, %ebp\n\t" + "andl $-16, %esp\n\t" // align the stack so engine can use sse + "pushl %ecx\n\t" + "pushl %edi\n\t" // opStack + "pushl %esi\n\t" // programStack + "pushl %eax\n\t" // syscallNum "call " CMANGFUNC(CallAsmCall) "\n\t" -// "addl $12, %%esp\n\t" - "movl %%ebp, %%esp\n\t" - "popl %%ebp\n\t" - "addl $4, %%edi\n\t" - "2:\n\t" -// "ret\n\t" + "addl $12, %esp\n\t" + "popl %ecx\n\t" + "movl %ebp, %esp\n\t" + "popl %ebp\n\t" + "addl $4, %edi\n\t" + "ret\n\t" #if defined __ELF__ -// ".size " CMANGFUNC(AsmCall)", .-" CMANGFUNC(AsmCall) + ".size " CMANGFUNC(AsmCall)", .-" CMANGFUNC(AsmCall) #endif - : - : "g" (currentVM->instructionCount), "g" (currentVM->instructionPointers) - : "%eax" ); -} - #endif static int Constant4( void ) { @@ -280,11 +250,6 @@ static int Constant4( void ) { return v; } -static int NextConstant4( void ) { - return (code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24)); -} - - static int Constant1( void ) { int v; @@ -455,150 +420,23 @@ qboolean EmitMovEBXEDI(vm_t *vm, int andit) { /* ================= -DoSyscall -Uses asm to get arguments from stack to work around different calling conventions -================= -*/ - -static void DoSyscall(void) -{ - vm_t *savedVM; - int *data; - - int syscallNum; - int programStack; - int *opStack; - - // Get arguments directly from registers to work around different calling conventions -#ifdef _MSC_VER - __asm - { - mov dword ptr syscallNum, eax - mov dword ptr programStack, esi - mov dword ptr opStack, edi - } -#else - __asm__ volatile( - "" - : "=a" (syscallNum), "=S" (programStack), "=D" (opStack) - : - : "memory" - ); -#endif - - // save currentVM allow for recursive VM entry - savedVM = currentVM; - data = (int *) (savedVM->dataBase + programStack + 4); - - // modify VM stack pointer for recursive VM entry - savedVM->programStack = programStack - 4; - *data = syscallNum; - opStack[1] = savedVM->systemCall(data); - - currentVM = savedVM; -} - -/* -================= -EmitVMCall -================= -*/ - -void EmitVMCall(void) -{ - EmitString("FF 25"); // jmp dword ptr [instructionPointers + 0x12345678] - Emit4((intptr_t) vm->instructionPointers + v*4); -} - -/* -================= -EmitSysCall -================= -*/ - -void EmitSysCall(void) -{ - EmitString("F7 D0"); // not eax - - // use ecx register to store DoSyscall address - EmitString("51"); // push ecx - EmitString("B1"); // mov ecx, [DoSyscall] - Emit4((intptr_t) DoSyscall); - - // align the stack pointer to a 16-byte-boundary - EmitString("55"); // push ebp - EmitString("89 E5"); // mov ebp, esp - EmitString("83 E4 F0"); // and esp, 0xFFFFFFF0 - - // call the syscall wrapper function - EmitString("FF D1"); // call ecx - - // reset the stack pointer to its previous value - EmitString("89 EC"); // mov esp, ebp - EmitString("5D"); // pop ebp - EmitString("59"); // pop ecx - - // have opStack reg point at return value - EmitString("83 C7 04"); // add edi, 4 -} - -/* -================= -EmitCall -================= -*/ - -void EmitCall(qboolean got_const, int cdest) -{ - if(got_const) - { - EmitString("B8"); // mov eax, cdest - Emit4(cdest); - - if(cdest < 0) - EmitSysCall(); - else - { - JUSED(cdest); - EmitVMCall(); - } - } - else - { -// EmitString(); - } - -} - -/* -================= VM_Compile ================= */ void VM_Compile( vm_t *vm, vmHeader_t *header ) { int op; - int op1; int maxLength; int v; int i; qboolean opt; - - jusedSize = header->instructionCount + 2; + int jusedSize = header->instructionCount + 2; // allocate a very large temp buffer, we will shrink it later maxLength = header->codeLength * 8; buf = Z_Malloc(maxLength); jused = Z_Malloc(jusedSize); - code = Z_Malloc(header->codeLength+32); Com_Memset(jused, 0, jusedSize); - Com_Memset(buf, 0, maxLength); - - // copy code in larger buffer and put some zeros at the end - // so we can safely look ahead for a few instructions in it - // without a chance to get false-positive because of some garbage bytes - Com_Memset(code, 0, header->codeLength+32); - Com_Memcpy(code, (byte *)header + header->codeOffset, header->codeLength ); // ensure that the optimisation pass knows about all the jump // table targets @@ -615,7 +453,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { // translate all instructions pc = 0; instruction = 0; - //code = (byte *)header + header->codeOffset; + code = (byte *)header + header->codeOffset; compiledOfs = 0; LastCommand = LAST_COMMAND_NONE; @@ -650,14 +488,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { Emit4( Constant4() ); break; case OP_CONST: - // we can safely perform optimizations only in case if - // we are 100% sure that next instruction is not a jump label - if ( !jused[instruction] && vm->jumpTableTargets ) - op1 = code[pc+4]; - else - op1 = OP_UNDEF; - - if ( op1 == OP_LOAD4 ) { + if (code[pc+4] == OP_LOAD4) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); @@ -667,7 +498,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_LOAD2 ) { + if (code[pc+4] == OP_LOAD2) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); @@ -677,7 +508,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_LOAD1 ) { + if (code[pc+4] == OP_LOAD1) { EmitAddEDI4(vm); EmitString( "BB" ); // mov ebx, 0x12345678 Emit4( (Constant4()&vm->dataMask) + (int)vm->dataBase); @@ -687,7 +518,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_STORE4 ) { + if (code[pc+4] == OP_STORE4) { opt = EmitMovEBXEDI(vm, (vm->dataMask & ~3)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); @@ -702,7 +533,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_STORE2 ) { + if (code[pc+4] == OP_STORE2) { opt = EmitMovEBXEDI(vm, (vm->dataMask & ~1)); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); @@ -717,7 +548,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_STORE1 ) { + if (code[pc+4] == OP_STORE1) { opt = EmitMovEBXEDI(vm, vm->dataMask); EmitString( "B8" ); // mov eax, 0x12345678 Emit4( Constant4() ); @@ -732,113 +563,20 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { instruction += 1; break; } - if ( op1 == OP_ADD ) { - v = NextConstant4(); - if ( v == 1 ) { - EmitString( "FF 07" ); // inc dword ptr [edi] - pc += 5; // OP_CONST + OP_ADD - instruction += 1; - break; - } + if (code[pc+4] == OP_ADD) { EmitString( "81 07" ); // add dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } - if ( op1 == OP_SUB ) { - v = NextConstant4(); - if ( v == 1 ) { - EmitString( "FF 0F" ); // dec dword ptr [edi] - pc += 5; // OP_CONST + OP_SUB - instruction += 1; - break; - } + if (code[pc+4] == OP_SUB) { EmitString( "81 2F" ); // sub dword ptr [edi], 0x1234567 Emit4( Constant4() ); pc++; // OP_ADD instruction += 1; break; } - if ( op1 == OP_LSH ) { - v = NextConstant4(); - if ( v >=1 && v <= 31 ) { - EmitString( "C1 27" ); // shl dword ptr [edi], 0x12 - Emit1( v ); - pc += 5; // OP_CONST + OP_LSH - instruction += 1; - break; - } - } - if ( op1 == OP_RSHI ) { - v = NextConstant4(); - if ( v >=1 && v <= 31 ) { - EmitString( "C1 3F" ); // sar dword ptr [edi], 0x12 - Emit1( v ); - pc += 5; // OP_CONST + OP_RSHI - instruction += 1; - break; - } - } - if ( op1 == OP_RSHU ) { - v = NextConstant4(); - if ( v >=1 && v <= 31 ) { - EmitString( "C1 2F" ); // shr dword ptr [edi], 0x12 - Emit1( v ); - pc += 5; // OP_CONST + OP_RSHU - instruction += 1; - break; - } - } - if ( op1 == OP_BAND ) { - v = NextConstant4(); - // try to generate shorter version - if ( v >= 0 && v <= 127 ) { - EmitString( "83 27" ); // and dword ptr[edi], 0x7F - Emit1( v ); - } else { - EmitString( "81 27" ); // and dword ptr[edi], 0x7FFFF - Emit4( v ); - } - pc += 5; // OP_CONST + OP_BAND - instruction += 1; - break; - } - if ( op1 == OP_BOR ) { - v = NextConstant4(); - // try to generate shorter version - if ( v >= 0 && v <= 127 ) { - EmitString( "83 0F" ); // or dword ptr[edi], 0x7F - Emit1( v ); - } else { - EmitString( "81 0F" ); // or dword ptr[edi], 0x7FFFF - Emit4( v ); - } - pc += 5; // OP_CONST + OP_BOR - instruction += 1; - break; - } - if ( op1 == OP_JUMP ) { - v = Constant4(); - JUSED(v); - EmitString( "FF 25" ); // jmp dword ptr [instructionPointers + 0x12345678] - Emit4( (intptr_t) vm->instructionPointers + v*4 ); - pc += 1; // OP_JUMP - instruction += 1; - break; - } - - if ( op1 == OP_CALL && NextConstant4() >= 0 ) { - v = Constant4(); - JUSED(v); - EmitString( "FF 15" ); // call dword ptr [instructionPointers + 0x12345678] - Emit4( (intptr_t) vm->instructionPointers + v*4 ); - EmitString( "8B 07" ); // mov eax, dword ptr [edi] - pc += 1; // OP_CALL - instruction += 1; - break; - } - EmitAddEDI4(vm); EmitString( "C7 07" ); // mov dword ptr [edi], 0x12345678 lastConst = Constant4(); @@ -858,10 +596,14 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { case OP_ARG: EmitMovEAXEDI(vm); // mov eax,dword ptr [edi] EmitString( "89 86" ); // mov dword ptr [esi+database],eax - Emit4((Constant1() & vm->dataMask & 0xFF) + (int)vm->dataBase); + // FIXME: range check + Emit4( Constant1() + (int)vm->dataBase ); EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 break; case OP_CALL: + EmitString( "C7 86" ); // mov dword ptr [esi+database],0x12345678 + Emit4( (int)vm->dataBase ); + Emit4( pc ); EmitString( "FF 15" ); // call asmCallPtr Emit4( (int)&asmCallPtr ); break; @@ -1335,15 +1077,11 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { break; case OP_JUMP: - EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 - EmitString( "8B 47 04" ); // mov eax,dword ptr [edi+4] - EmitString( "3B 05" ); // cmp eax,[callMask] - Emit4( (int)&vm->instructionCount ); - EmitString( "73 07" ); // jae +7 - EmitString( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4] + EmitCommand(LAST_COMMAND_SUB_DI_4); // sub edi, 4 + EmitString( "8B 47 04" ); // mov eax,dword ptr [edi+4] + // FIXME: range check + EmitString( "FF 24 85" ); // jmp dword ptr [instructionPointers + eax * 4] Emit4( (int)vm->instructionPointers ); - EmitString( "FF 15" ); // call errJumpPtr - Emit4( (int)&errJumpPtr ); break; default: VMFREE_BUFFERS(); @@ -1354,7 +1092,7 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { } } - // copy to an exact sized buffer with the appropriate permission bits + // copy to an exact size buffer on the hunk vm->codeLength = compiledOfs; #ifdef VM_X86_MMAP vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); @@ -1386,7 +1124,6 @@ void VM_Compile( vm_t *vm, vmHeader_t *header ) { } #endif - Z_Free( code ); Z_Free( buf ); Z_Free( jused ); Com_Printf( "VM file %s compiled to %i bytes of code\n", vm->name, compiledOfs ); @@ -1424,12 +1161,18 @@ int VM_CallCompiled( vm_t *vm, int *args ) { int stackOnEntry; byte *image; void *opStack; + int *oldInstructionPointers; + + oldInstructionPointers = instructionPointers; currentVM = vm; + instructionPointers = vm->instructionPointers; // interpret the code vm->currentlyInterpreting = qtrue; + callMask = vm->dataMask; + // we might be called recursively, so this might not be the very top programStack = vm->programStack; stackOnEntry = programStack; @@ -1497,5 +1240,8 @@ int VM_CallCompiled( vm_t *vm, int *args ) { vm->programStack = stackOnEntry; + // in case we were recursively called by another vm + instructionPointers = oldInstructionPointers; + return *(int *)opStack; } |