1 /*
2 ===========================================================================
3 Copyright (C) 1999-2005 Id Software, Inc.
4 Copyright (C) 2005 Ludwig Nussel <ludwig.nussel@web.de>
5
6 This file is part of Quake III Arena source code.
7
8 Quake III Arena source code is free software; you can redistribute it
9 and/or modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of the License,
11 or (at your option) any later version.
12
13 Quake III Arena source code is distributed in the hope that it will be
14 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with Quake III Arena source code; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ===========================================================================
22 */
23 // vm_x86_64.c -- load time compiler and execution environment for x86-64
24
25 #include "vm_local.h"
26
27 #include <sys/mman.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 #include <sys/time.h>
32 #include <time.h>
33 #include <fcntl.h>
34 #include <errno.h>
35 #include <unistd.h>
36 #include <stdarg.h>
37
38 //#define USE_GAS
39 //#define DEBUG_VM
40
41 #ifdef DEBUG_VM
42 #define Dfprintf(fd, args...) fprintf(fd, ##args)
43 static FILE* qdasmout;
44 #else
45 #define Dfprintf(args...)
46 #endif
47
48 #define VM_X86_64_MMAP
49
50 #ifndef USE_GAS
51 void assembler_set_output(char* buf);
52 size_t assembler_get_code_size(void);
53 void assembler_init(int pass);
54 void assemble_line(const char* input, size_t len);
55 #ifdef Dfprintf
56 #undef Dfprintf
57 #define Dfprintf(args...)
58 #endif
59 #endif // USE_GAS
60
61 static void VM_Destroy_Compiled(vm_t* self);
62
63 /*
64
65 |=====================|
66 ^ dataMask ^- programStack rdi
67 |
68 +- r8
69
70 eax scratch
71 ebx scratch
72 ecx scratch (required for shifts)
73 edx scratch (required for divisions)
74 rsi stack pointer (opStack)
75 rdi program frame pointer (programStack)
76 r8 pointer data (vm->dataBase)
77 r10 start of generated code
78 */
79
80
callAsmCall(long callProgramStack,long callSyscallNum)81 static long callAsmCall(long callProgramStack, long callSyscallNum)
82 {
83 vm_t *savedVM;
84 long ret = 0x77;
85 long args[11];
86 // int iargs[11];
87 int i;
88
89 // Dfprintf(stderr, "callAsmCall(%ld, %ld)\n", callProgramStack, callSyscallNum);
90 // Com_Printf("-> callAsmCall %s, level %d, num %ld\n", currentVM->name, currentVM->callLevel, callSyscallNum);
91
92 savedVM = currentVM;
93
94 // save the stack to allow recursive VM entry
95 currentVM->programStack = callProgramStack - 4;
96
97 args[0] = callSyscallNum;
98 // iargs[0] = callSyscallNum;
99 for(i = 0; i < 10; ++i)
100 {
101 // iargs[i+1] = *(int *)((byte *)currentVM->dataBase + callProgramStack + 8 + 4*i);
102 args[i+1] = *(int *)((byte *)currentVM->dataBase + callProgramStack + 8 + 4*i);
103 }
104 ret = currentVM->systemCall(args);
105
106 currentVM = savedVM;
107 // Com_Printf("<- callAsmCall %s, level %d, num %ld\n", currentVM->name, currentVM->callLevel, callSyscallNum);
108
109 return ret;
110 }
111
112 #ifdef DEBUG_VM
113 static char *opnames[256] = {
114 "OP_UNDEF",
115
116 "OP_IGNORE",
117
118 "OP_BREAK",
119
120 "OP_ENTER",
121 "OP_LEAVE",
122 "OP_CALL",
123 "OP_PUSH",
124 "OP_POP",
125
126 "OP_CONST",
127
128 "OP_LOCAL",
129
130 "OP_JUMP",
131
132 //-------------------
133
134 "OP_EQ",
135 "OP_NE",
136
137 "OP_LTI",
138 "OP_LEI",
139 "OP_GTI",
140 "OP_GEI",
141
142 "OP_LTU",
143 "OP_LEU",
144 "OP_GTU",
145 "OP_GEU",
146
147 "OP_EQF",
148 "OP_NEF",
149
150 "OP_LTF",
151 "OP_LEF",
152 "OP_GTF",
153 "OP_GEF",
154
155 //-------------------
156
157 "OP_LOAD1",
158 "OP_LOAD2",
159 "OP_LOAD4",
160 "OP_STORE1",
161 "OP_STORE2",
162 "OP_STORE4",
163 "OP_ARG",
164
165 "OP_BLOCK_COPY",
166
167 //-------------------
168
169 "OP_SEX8",
170 "OP_SEX16",
171
172 "OP_NEGI",
173 "OP_ADD",
174 "OP_SUB",
175 "OP_DIVI",
176 "OP_DIVU",
177 "OP_MODI",
178 "OP_MODU",
179 "OP_MULI",
180 "OP_MULU",
181
182 "OP_BAND",
183 "OP_BOR",
184 "OP_BXOR",
185 "OP_BCOM",
186
187 "OP_LSH",
188 "OP_RSHI",
189 "OP_RSHU",
190
191 "OP_NEGF",
192 "OP_ADDF",
193 "OP_SUBF",
194 "OP_DIVF",
195 "OP_MULF",
196
197 "OP_CVIF",
198 "OP_CVFI"
199 };
200 #endif // DEBUG_VM
201
202 static unsigned char op_argsize[256] =
203 {
204 [OP_ENTER] = 4,
205 [OP_LEAVE] = 4,
206 [OP_CONST] = 4,
207 [OP_LOCAL] = 4,
208 [OP_EQ] = 4,
209 [OP_NE] = 4,
210 [OP_LTI] = 4,
211 [OP_LEI] = 4,
212 [OP_GTI] = 4,
213 [OP_GEI] = 4,
214 [OP_LTU] = 4,
215 [OP_LEU] = 4,
216 [OP_GTU] = 4,
217 [OP_GEU] = 4,
218 [OP_EQF] = 4,
219 [OP_NEF] = 4,
220 [OP_LTF] = 4,
221 [OP_LEF] = 4,
222 [OP_GTF] = 4,
223 [OP_GEF] = 4,
224 [OP_ARG] = 1,
225 [OP_BLOCK_COPY] = 4,
226 };
227
228 #ifdef USE_GAS
229 #define emit(x...) \
230 do { fprintf(fh_s, ##x); fputc('\n', fh_s); } while(0)
231 #else
emit(const char * fmt,...)232 void emit(const char* fmt, ...)
233 {
234 va_list ap;
235 char line[4096];
236 va_start(ap, fmt);
237 Q_vsnprintf(line, sizeof(line), fmt, ap);
238 va_end(ap);
239 assemble_line(line, strlen(line));
240 }
241 #endif // USE_GAS
242
243 #ifdef USE_GAS
244 #define JMPIARG \
245 emit("jmp i_%08x", iarg);
246 #else
247 #define JMPIARG \
248 emit("movq $%lu, %%rax", vm->codeBase+vm->instructionPointers[iarg]); \
249 emit("jmpq *%%rax");
250 #endif
251
252 // integer compare and jump
253 #define IJ(op) \
254 emit("subq $8, %%rsi"); \
255 emit("movl 4(%%rsi), %%eax"); \
256 emit("cmpl 8(%%rsi), %%eax"); \
257 emit(op " i_%08x", instruction+1); \
258 JMPIARG \
259 neednilabel = 1;
260
261 #ifdef USE_X87
262 #define FJ(bits, op) \
263 emit("subq $8, %%rsi");\
264 emit("flds 4(%%rsi)");\
265 emit("fcomps 8(%%rsi)");\
266 emit("fnstsw %%ax");\
267 emit("testb $" #bits ", %%ah");\
268 emit(op " i_%08x", instruction+1);\
269 JMPIARG \
270 neednilabel = 1;
271 #define XJ(x)
272 #else
273 #define FJ(x, y)
274 #define XJ(op) \
275 emit("subq $8, %%rsi");\
276 emit("movss 4(%%rsi), %%xmm0");\
277 emit("ucomiss 8(%%rsi), %%xmm0");\
278 emit("jp i_%08x", instruction+1);\
279 emit(op " i_%08x", instruction+1);\
280 JMPIARG \
281 neednilabel = 1;
282 #endif
283
284 #define SIMPLE(op) \
285 emit("subq $4, %%rsi"); \
286 emit("movl 4(%%rsi), %%eax"); \
287 emit(op " %%eax, 0(%%rsi)");
288
289 #ifdef USE_X87
290 #define FSIMPLE(op) \
291 emit("subq $4, %%rsi"); \
292 emit("flds 0(%%rsi)"); \
293 emit(op " 4(%%rsi)"); \
294 emit("fstps 0(%%rsi)");
295 #define XSIMPLE(op)
296 #else
297 #define FSIMPLE(op)
298 #define XSIMPLE(op) \
299 emit("subq $4, %%rsi"); \
300 emit("movss 0(%%rsi), %%xmm0"); \
301 emit(op " 4(%%rsi), %%xmm0"); \
302 emit("movss %%xmm0, 0(%%rsi)");
303 #endif
304
305 #define SHIFT(op) \
306 emit("subq $4, %%rsi"); \
307 emit("movl 4(%%rsi), %%ecx"); \
308 emit("movl 0(%%rsi), %%eax"); \
309 emit(op " %%cl, %%eax"); \
310 emit("movl %%eax, 0(%%rsi)");
311
312 #if 1
313 #define RANGECHECK(reg) \
314 emit("andl $0x%x, %%" #reg, vm->dataMask);
315 #elif 0
316 #define RANGECHECK(reg) \
317 emit("pushl %%" #reg); \
318 emit("andl $0x%x, %%" #reg, ~vm->dataMask); \
319 emit("jz rangecheck_ok_i_%08x", instruction); \
320 emit("int3"); \
321 emit("rangecheck_ok_i_%08x:", instruction); \
322 emit("popl %%" #reg); \
323 emit("andl $0x%x, %%" #reg, vm->dataMask);
324 #else
325 #define RANGECHECK(reg)
326 #endif
327
328 #ifdef DEBUG_VM
329 #define NOTIMPL(x) \
330 do { Com_Error(ERR_DROP, "instruction not implemented: %s\n", opnames[x]); } while(0)
331 #else
332 #define NOTIMPL(x) \
333 do { Com_Printf(S_COLOR_RED "instruction not implemented: %x\n", x); vm->compiled = qfalse; return; } while(0)
334 #endif
335
getentrypoint(vm_t * vm)336 static void* getentrypoint(vm_t* vm)
337 {
338 #ifdef USE_GAS
339 return vm->codeBase+64; // skip ELF header
340 #else
341 return vm->codeBase;
342 #endif // USE_GAS
343 }
344
345 #ifdef USE_GAS
mmapfile(const char * fn,size_t * size)346 char* mmapfile(const char* fn, size_t* size)
347 {
348 int fd = -1;
349 char* mem = NULL;
350 struct stat stb;
351
352 fd = open(fn, O_RDONLY);
353 if(fd == -1)
354 goto out;
355
356 if(fstat(fd, &stb) == -1)
357 goto out;
358
359 *size = stb.st_size;
360
361 mem = mmap(NULL, stb.st_size, PROT_READ|PROT_EXEC, MAP_SHARED, fd, 0);
362 if(mem == (void*)-1)
363 mem = NULL;
364
365 out:
366 if(fd != -1)
367 close(fd);
368
369 return mem;
370 }
371
doas(char * in,char * out,unsigned char ** compiledcode)372 static int doas(char* in, char* out, unsigned char** compiledcode)
373 {
374 unsigned char* mem;
375 size_t size = -1;
376 pid_t pid;
377
378 Com_Printf("running assembler < %s > %s\n", in, out);
379 pid = fork();
380 if(pid == -1)
381 {
382 Com_Printf(S_COLOR_RED "can't fork\n");
383 return -1;
384 }
385
386 if(!pid)
387 {
388 char* const argv[] = {
389 "as",
390 "-o",
391 out,
392 in,
393 NULL
394 };
395
396 execvp(argv[0], argv);
397 _exit(-1);
398 }
399 else
400 {
401 int status;
402 if(waitpid(pid, &status, 0) == -1)
403 {
404 Com_Printf(S_COLOR_RED "can't wait for as: %s\n", strerror(errno));
405 return -1;
406 }
407
408 if(!WIFEXITED(status))
409 {
410 Com_Printf(S_COLOR_RED "as died\n");
411 return -1;
412 }
413 if(WEXITSTATUS(status))
414 {
415 Com_Printf(S_COLOR_RED "as failed with status %d\n", WEXITSTATUS(status));
416 return -1;
417 }
418 }
419
420 Com_Printf("done\n");
421
422 mem = (unsigned char*)mmapfile(out, &size);
423 if(!mem)
424 {
425 Com_Printf(S_COLOR_RED "can't mmap object file %s: %s\n", out, strerror(errno));
426 return -1;
427 }
428
429 *compiledcode = mem;
430
431 return size;
432 }
433 #endif // USE_GAS
434
block_copy_vm(unsigned dest,unsigned src,unsigned count)435 static void block_copy_vm(unsigned dest, unsigned src, unsigned count)
436 {
437 unsigned dataMask = currentVM->dataMask;
438
439 if ((dest & dataMask) != dest
440 || (src & dataMask) != src
441 || ((dest+count) & dataMask) != dest + count
442 || ((src+count) & dataMask) != src + count)
443 {
444 Com_Error(ERR_DROP, "OP_BLOCK_COPY out of range!\n");
445 }
446
447 memcpy(currentVM->dataBase+dest, currentVM->dataBase+src, count);
448 }
449
450 /*
451 =================
452 VM_Compile
453 =================
454 */
VM_Compile(vm_t * vm,vmHeader_t * header)455 void VM_Compile( vm_t *vm, vmHeader_t *header ) {
456 unsigned char op;
457 int pc;
458 unsigned instruction;
459 char* code;
460 unsigned iarg = 0;
461 unsigned char barg = 0;
462 int neednilabel = 0;
463 struct timeval tvstart = {0, 0};
464
465 #ifdef USE_GAS
466 byte* compiledcode;
467 int compiledsize;
468 void* entryPoint;
469 char fn_s[2*MAX_QPATH]; // output file for assembler code
470 char fn_o[2*MAX_QPATH]; // file written by as
471 #ifdef DEBUG_VM
472 char fn_d[MAX_QPATH]; // disassembled
473 #endif
474 FILE* fh_s;
475 int fd_s, fd_o;
476
477 gettimeofday(&tvstart, NULL);
478
479 Com_Printf("compiling %s\n", vm->name);
480
481 #ifdef DEBUG_VM
482 snprintf(fn_s, sizeof(fn_s), "%.63s.s", vm->name);
483 snprintf(fn_o, sizeof(fn_o), "%.63s.o", vm->name);
484 fd_s = open(fn_s, O_CREAT|O_WRONLY|O_TRUNC, 0644);
485 fd_o = open(fn_o, O_CREAT|O_WRONLY|O_TRUNC, 0644);
486 #else
487 snprintf(fn_s, sizeof(fn_s), "/tmp/%.63s.s_XXXXXX", vm->name);
488 snprintf(fn_o, sizeof(fn_o), "/tmp/%.63s.o_XXXXXX", vm->name);
489 fd_s = mkstemp(fn_s);
490 fd_o = mkstemp(fn_o);
491 #endif
492 if(fd_s == -1 || fd_o == -1)
493 {
494 if(fd_s != -1) close(fd_s);
495 if(fd_o != -1) close(fd_o);
496 unlink(fn_s);
497 unlink(fn_o);
498
499 Com_Printf(S_COLOR_RED "can't create temporary file %s for vm\n", fn_s);
500 vm->compiled = qfalse;
501 return;
502 }
503
504 #ifdef DEBUG_VM
505 strcpy(fn_d,vm->name);
506 strcat(fn_d, ".qdasm");
507
508 qdasmout = fopen(fn_d, "w");
509 #endif
510
511 fh_s = fdopen(fd_s, "wb");
512 if(!fh_s)
513 {
514 Com_Printf(S_COLOR_RED "can't write %s\n", fn_s);
515 vm->compiled = qfalse;
516 return;
517 }
518
519 emit("start:");
520 emit("or %%r8, %%r8"); // check whether to set up instruction pointers
521 emit("jnz main");
522 emit("jmp setupinstructionpointers");
523
524 emit("main:");
525 #else // USE_GAS
526 int pass;
527 size_t compiledOfs = 0;
528
529 gettimeofday(&tvstart, NULL);
530
531 for (pass = 0; pass < 2; ++pass) {
532
533 if(pass)
534 {
535 compiledOfs = assembler_get_code_size();
536 vm->codeLength = compiledOfs;
537 vm->codeBase = mmap(NULL, compiledOfs, PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
538 if(vm->codeBase == (void*)-1)
539 Com_Error(ERR_DROP, "VM_CompileX86: can't mmap memory");
540
541 assembler_set_output((char*)vm->codeBase);
542 }
543
544 assembler_init(pass);
545
546 #endif // USE_GAS
547
548 // translate all instructions
549 pc = 0;
550 code = (char *)header + header->codeOffset;
551
552 for ( instruction = 0; instruction < header->instructionCount; ++instruction )
553 {
554 op = code[ pc ];
555 ++pc;
556
557 #ifndef USE_GAS
558 vm->instructionPointers[instruction] = assembler_get_code_size();
559 #endif
560
561 /* store current instruction number in r15 for debugging */
562 #if 1
563 emit("nop");
564 emit("movq $%d, %%r15", instruction);
565 emit("nop");
566 #endif
567
568 if(op_argsize[op] == 4)
569 {
570 iarg = *(int*)(code+pc);
571 pc += 4;
572 Dfprintf(qdasmout, "%s %8u\n", opnames[op], iarg);
573 }
574 else if(op_argsize[op] == 1)
575 {
576 barg = code[pc++];
577 Dfprintf(qdasmout, "%s %8hhu\n", opnames[op], barg);
578 }
579 else
580 {
581 Dfprintf(qdasmout, "%s\n", opnames[op]);
582 }
583
584 #ifdef USE_GAS
585 emit("i_%08x:", instruction);
586 #else
587 if(neednilabel)
588 {
589 emit("i_%08x:", instruction);
590 neednilabel = 0;
591 }
592 #endif
593
594 switch ( op )
595 {
596 case OP_UNDEF:
597 NOTIMPL(op);
598 break;
599 case OP_IGNORE:
600 emit("nop");
601 break;
602 case OP_BREAK:
603 emit("int3");
604 break;
605 case OP_ENTER:
606 emit("subl $%d, %%edi", iarg);
607 RANGECHECK(edi);
608 break;
609 case OP_LEAVE:
610 emit("addl $%d, %%edi", iarg); // get rid of stack frame
611 emit("ret");
612 break;
613 case OP_CALL:
614 emit("movl 0(%%rsi), %%eax"); // get instr from stack
615 emit("subq $4, %%rsi");
616 emit("movl $%d, 0(%%r8, %%rdi, 1)", instruction+1); // save next instruction
617 emit("orl %%eax, %%eax");
618 emit("jl callSyscall%d", instruction);
619 emit("movq $%lu, %%rbx", (unsigned long)vm->instructionPointers);
620 emit("movl (%%rbx, %%rax, 4), %%eax"); // load new relative jump address
621 emit("addq %%r10, %%rax");
622 emit("callq *%%rax");
623 emit("jmp i_%08x", instruction+1);
624 emit("callSyscall%d:", instruction);
625 // emit("fnsave 4(%%rsi)");
626 emit("push %%rsi");
627 emit("push %%rdi");
628 emit("push %%r8");
629 emit("push %%r9");
630 emit("push %%r10");
631 emit("movq %%rsp, %%rbx"); // we need to align the stack pointer
632 emit("subq $8, %%rbx"); // |
633 emit("andq $127, %%rbx"); // |
634 emit("subq %%rbx, %%rsp"); // <-+
635 emit("push %%rbx");
636 emit("negl %%eax"); // convert to actual number
637 emit("decl %%eax");
638 // first argument already in rdi
639 emit("movq %%rax, %%rsi"); // second argument in rsi
640 emit("movq $%lu, %%rax", (unsigned long)callAsmCall);
641 emit("callq *%%rax");
642 emit("pop %%rbx");
643 emit("addq %%rbx, %%rsp");
644 emit("pop %%r10");
645 emit("pop %%r9");
646 emit("pop %%r8");
647 emit("pop %%rdi");
648 emit("pop %%rsi");
649 // emit("frstor 4(%%rsi)");
650 emit("addq $4, %%rsi");
651 emit("movl %%eax, (%%rsi)"); // store return value
652 neednilabel = 1;
653 break;
654 case OP_PUSH:
655 emit("addq $4, %%rsi");
656 break;
657 case OP_POP:
658 emit("subq $4, %%rsi");
659 break;
660 case OP_CONST:
661 emit("addq $4, %%rsi");
662 emit("movl $%d, 0(%%rsi)", iarg);
663 break;
664 case OP_LOCAL:
665 emit("movl %%edi, %%ebx");
666 emit("addl $%d,%%ebx", iarg);
667 emit("addq $4, %%rsi");
668 emit("movl %%ebx, 0(%%rsi)");
669 break;
670 case OP_JUMP:
671 emit("movl 0(%%rsi), %%eax"); // get instr from stack
672 emit("subq $4, %%rsi");
673 emit("movq $%lu, %%rbx", (unsigned long)vm->instructionPointers);
674 emit("movl (%%rbx, %%rax, 4), %%eax"); // load new relative jump address
675 emit("addq %%r10, %%rax");
676 emit("jmp *%%rax");
677 break;
678 case OP_EQ:
679 IJ("jne");
680 break;
681 case OP_NE:
682 IJ("je");
683 break;
684 case OP_LTI:
685 IJ("jnl");
686 break;
687 case OP_LEI:
688 IJ("jnle");
689 break;
690 case OP_GTI:
691 IJ("jng");
692 break;
693 case OP_GEI:
694 IJ("jnge");
695 break;
696 case OP_LTU:
697 IJ("jnb");
698 break;
699 case OP_LEU:
700 IJ("jnbe");
701 break;
702 case OP_GTU:
703 IJ("jna");
704 break;
705 case OP_GEU:
706 IJ("jnae");
707 break;
708 case OP_EQF:
709 FJ(0x40, "jz");
710 XJ("jnz");
711 break;
712 case OP_NEF:
713 FJ(0x40, "jnz");
714 #ifndef USE_X87
715 emit("subq $8, %%rsi");
716 emit("movss 4(%%rsi), %%xmm0");
717 emit("ucomiss 8(%%rsi), %%xmm0");
718 emit("jp dojump_i_%08x", instruction);
719 emit("jz i_%08x", instruction+1);
720 emit("dojump_i_%08x:", instruction);
721 JMPIARG
722 neednilabel = 1;
723 #endif
724 break;
725 case OP_LTF:
726 FJ(0x01, "jz");
727 XJ("jnc");
728 break;
729 case OP_LEF:
730 FJ(0x41, "jz");
731 XJ("ja");
732 break;
733 case OP_GTF:
734 FJ(0x41, "jnz");
735 XJ("jbe");
736 break;
737 case OP_GEF:
738 FJ(0x01, "jnz");
739 XJ("jb");
740 break;
741 case OP_LOAD1:
742 emit("movl 0(%%rsi), %%eax"); // get value from stack
743 RANGECHECK(eax);
744 emit("movb 0(%%r8, %%rax, 1), %%al"); // deref into eax
745 emit("andq $255, %%rax");
746 emit("movl %%eax, 0(%%rsi)"); // store on stack
747 break;
748 case OP_LOAD2:
749 emit("movl 0(%%rsi), %%eax"); // get value from stack
750 RANGECHECK(eax);
751 emit("movw 0(%%r8, %%rax, 1), %%ax"); // deref into eax
752 emit("movl %%eax, 0(%%rsi)"); // store on stack
753 break;
754 case OP_LOAD4:
755 emit("movl 0(%%rsi), %%eax"); // get value from stack
756 RANGECHECK(eax); // not a pointer!?
757 emit("movl 0(%%r8, %%rax, 1), %%eax"); // deref into eax
758 emit("movl %%eax, 0(%%rsi)"); // store on stack
759 break;
760 case OP_STORE1:
761 emit("movl 0(%%rsi), %%eax"); // get value from stack
762 emit("andq $255, %%rax");
763 emit("movl -4(%%rsi), %%ebx"); // get pointer from stack
764 RANGECHECK(ebx);
765 emit("movb %%al, 0(%%r8, %%rbx, 1)"); // store in memory
766 emit("subq $8, %%rsi");
767 break;
768 case OP_STORE2:
769 emit("movl 0(%%rsi), %%eax"); // get value from stack
770 emit("movl -4(%%rsi), %%ebx"); // get pointer from stack
771 RANGECHECK(ebx);
772 emit("movw %%ax, 0(%%r8, %%rbx, 1)"); // store in memory
773 emit("subq $8, %%rsi");
774 break;
775 case OP_STORE4:
776 emit("movl -4(%%rsi), %%ebx"); // get pointer from stack
777 RANGECHECK(ebx);
778 emit("movl 0(%%rsi), %%ecx"); // get value from stack
779 emit("movl %%ecx, 0(%%r8, %%rbx, 1)"); // store in memory
780 emit("subq $8, %%rsi");
781 break;
782 case OP_ARG:
783 emit("subq $4, %%rsi");
784 emit("movl 4(%%rsi), %%eax"); // get value from stack
785 emit("movl $0x%hhx, %%ebx", barg);
786 emit("addl %%edi, %%ebx");
787 RANGECHECK(ebx);
788 emit("movl %%eax, 0(%%r8,%%rbx, 1)"); // store in args space
789 break;
790 case OP_BLOCK_COPY:
791
792 emit("subq $8, %%rsi");
793 emit("push %%rsi");
794 emit("push %%rdi");
795 emit("push %%r8");
796 emit("push %%r9");
797 emit("push %%r10");
798 emit("movl 4(%%rsi), %%edi"); // 1st argument dest
799 emit("movl 8(%%rsi), %%esi"); // 2nd argument src
800 emit("movl $%d, %%edx", iarg); // 3rd argument count
801 emit("movq $%lu, %%rax", (unsigned long)block_copy_vm);
802 emit("callq *%%rax");
803 emit("pop %%r10");
804 emit("pop %%r9");
805 emit("pop %%r8");
806 emit("pop %%rdi");
807 emit("pop %%rsi");
808
809 break;
810 case OP_SEX8:
811 emit("movw 0(%%rsi), %%ax");
812 emit("andq $255, %%rax");
813 emit("cbw");
814 emit("cwde");
815 emit("movl %%eax, 0(%%rsi)");
816 break;
817 case OP_SEX16:
818 emit("movw 0(%%rsi), %%ax");
819 emit("cwde");
820 emit("movl %%eax, 0(%%rsi)");
821 break;
822 case OP_NEGI:
823 emit("negl 0(%%rsi)");
824 break;
825 case OP_ADD:
826 SIMPLE("addl");
827 break;
828 case OP_SUB:
829 SIMPLE("subl");
830 break;
831 case OP_DIVI:
832 emit("subq $4, %%rsi");
833 emit("movl 0(%%rsi), %%eax");
834 emit("cdq");
835 emit("idivl 4(%%rsi)");
836 emit("movl %%eax, 0(%%rsi)");
837 break;
838 case OP_DIVU:
839 emit("subq $4, %%rsi");
840 emit("movl 0(%%rsi), %%eax");
841 emit("xorq %%rdx, %%rdx");
842 emit("divl 4(%%rsi)");
843 emit("movl %%eax, 0(%%rsi)");
844 break;
845 case OP_MODI:
846 emit("subq $4, %%rsi");
847 emit("movl 0(%%rsi), %%eax");
848 emit("xorl %%edx, %%edx");
849 emit("cdq");
850 emit("idivl 4(%%rsi)");
851 emit("movl %%edx, 0(%%rsi)");
852 break;
853 case OP_MODU:
854 emit("subq $4, %%rsi");
855 emit("movl 0(%%rsi), %%eax");
856 emit("xorl %%edx, %%edx");
857 emit("divl 4(%%rsi)");
858 emit("movl %%edx, 0(%%rsi)");
859 break;
860 case OP_MULI:
861 emit("subq $4, %%rsi");
862 emit("movl 0(%%rsi), %%eax");
863 emit("imull 4(%%rsi)");
864 emit("movl %%eax, 0(%%rsi)");
865 break;
866 case OP_MULU:
867 emit("subq $4, %%rsi");
868 emit("movl 0(%%rsi), %%eax");
869 emit("mull 4(%%rsi)");
870 emit("movl %%eax, 0(%%rsi)");
871 break;
872 case OP_BAND:
873 SIMPLE("andl");
874 break;
875 case OP_BOR:
876 SIMPLE("orl");
877 break;
878 case OP_BXOR:
879 SIMPLE("xorl");
880 break;
881 case OP_BCOM:
882 emit("notl 0(%%rsi)");
883 break;
884 case OP_LSH:
885 SHIFT("shl");
886 break;
887 case OP_RSHI:
888 SHIFT("sarl");
889 break;
890 case OP_RSHU:
891 SHIFT("shrl");
892 break;
893 case OP_NEGF:
894 #ifdef USE_X87
895 emit("flds 0(%%rsi)");
896 emit("fchs");
897 emit("fstps 0(%%rsi)");
898 #else
899 emit("movl $0x80000000, %%eax");
900 emit("xorl %%eax, 0(%%rsi)");
901 #endif
902 break;
903 case OP_ADDF:
904 FSIMPLE("fadds");
905 XSIMPLE("addss");
906 break;
907 case OP_SUBF:
908 FSIMPLE("fsubs");
909 XSIMPLE("subss");
910 break;
911 case OP_DIVF:
912 FSIMPLE("fdivs");
913 XSIMPLE("divss");
914 break;
915 case OP_MULF:
916 FSIMPLE("fmuls");
917 XSIMPLE("mulss");
918 break;
919 case OP_CVIF:
920 #ifdef USE_X87
921 emit("filds 0(%%rsi)");
922 emit("fstps 0(%%rsi)");
923 #else
924 emit("movl 0(%%rsi), %%eax");
925 emit("cvtsi2ss %%eax, %%xmm0");
926 emit("movss %%xmm0, 0(%%rsi)");
927 #endif
928 break;
929 case OP_CVFI:
930 #ifdef USE_X87
931 emit("flds 0(%%rsi)");
932 emit("fnstcw 4(%%rsi)");
933 emit("movw $0x0F7F, 8(%%rsi)"); // round toward zero
934 emit("fldcw 8(%%rsi)");
935 emit("fistpl 0(%%rsi)");
936 emit("fldcw 4(%%rsi)");
937 #else
938 emit("movss 0(%%rsi), %%xmm0");
939 emit("cvttss2si %%xmm0, %%eax");
940 emit("movl %%eax, 0(%%rsi)");
941 #endif
942 break;
943 default:
944 NOTIMPL(op);
945 break;
946 }
947 }
948
949 #ifdef USE_GAS
950 emit("setupinstructionpointers:");
951 emit("movq $%lu, %%rax", (unsigned long)vm->instructionPointers);
952 for ( instruction = 0; instruction < header->instructionCount; ++instruction )
953 {
954 emit("movl $i_%08x-start, %d(%%rax)", instruction, instruction*4);
955 }
956 emit("ret");
957
958 emit("debugger:");
959 if(1);
960 {
961 int i = 6;
962 while(i--)
963 {
964 emit("nop");
965 emit("int3");
966 }
967 }
968
969 fflush(fh_s);
970 fclose(fh_s);
971
972 compiledsize = doas(fn_s, fn_o, &compiledcode);
973 if(compiledsize == -1)
974 {
975 vm->compiled = qfalse;
976 goto out;
977 }
978
979 vm->codeBase = compiledcode; // remember to skip ELF header!
980 vm->codeLength = compiledsize;
981
982 #else // USE_GAS
983 }
984 assembler_init(0);
985
986 if(mprotect(vm->codeBase, compiledOfs, PROT_READ|PROT_EXEC))
987 Com_Error(ERR_DROP, "VM_CompileX86: mprotect failed");
988 #endif // USE_GAS
989
990 vm->destroy = VM_Destroy_Compiled;
991
992 #ifdef USE_GAS
993 entryPoint = getentrypoint(vm);
994
995 // __asm__ __volatile__ ("int3");
996 Com_Printf("computing jump table\n");
997
998 // call code with r8 set to zero to set up instruction pointers
999 __asm__ __volatile__ (
1000 " xorq %%r8,%%r8 \r\n" \
1001 " movq %0,%%r10 \r\n" \
1002 " callq *%%r10 \r\n" \
1003 :
1004 : "m" (entryPoint)
1005 : "%r8", "%r10", "%rax"
1006 );
1007
1008 #ifdef DEBUG_VM
1009 fflush(qdasmout);
1010 fclose(qdasmout);
1011 #endif
1012
1013 out:
1014 close(fd_o);
1015
1016 #ifndef DEBUG_VM
1017 if(!com_developer->integer)
1018 {
1019 unlink(fn_o);
1020 unlink(fn_s);
1021 }
1022 #endif
1023 #endif // USE_GAS
1024
1025 if(vm->compiled)
1026 {
1027 struct timeval tvdone = {0, 0};
1028 struct timeval dur = {0, 0};
1029 Com_Printf( "VM file %s compiled to %i bytes of code (%p - %p)\n", vm->name, vm->codeLength, vm->codeBase, vm->codeBase+vm->codeLength );
1030
1031 gettimeofday(&tvdone, NULL);
1032 timersub(&tvdone, &tvstart, &dur);
1033 Com_Printf( "compilation took %lu.%06lu seconds\n", dur.tv_sec, dur.tv_usec );
1034 }
1035 }
1036
1037
VM_Destroy_Compiled(vm_t * self)1038 void VM_Destroy_Compiled(vm_t* self)
1039 {
1040 #ifdef USE_GAS
1041 munmap(self->codeBase, self->codeLength);
1042 #elif _WIN32
1043 VirtualFree(self->codeBase, self->codeLength, MEM_RELEASE);
1044 #else
1045 munmap(self->codeBase, self->codeLength);
1046 #endif
1047 }
1048
1049 /*
1050 ==============
1051 VM_CallCompiled
1052
1053 This function is called directly by the generated code
1054 ==============
1055 */
1056
1057 #ifdef DEBUG_VM
1058 static char* memData;
1059 #endif
1060
VM_CallCompiled(vm_t * vm,int * args)1061 int VM_CallCompiled( vm_t *vm, int *args ) {
1062 int programCounter;
1063 int programStack;
1064 int stackOnEntry;
1065 byte *image;
1066 void *entryPoint;
1067 void *opStack;
1068 int stack[1024] = { 0xDEADBEEF };
1069
1070 currentVM = vm;
1071
1072 // Com_Printf("entering %s level %d, call %d, arg1 = 0x%x\n", vm->name, vm->callLevel, args[0], args[1]);
1073
1074 // interpret the code
1075 vm->currentlyInterpreting = qtrue;
1076
1077 // callMask = vm->dataMask;
1078
1079 // we might be called recursively, so this might not be the very top
1080 programStack = vm->programStack;
1081 stackOnEntry = programStack;
1082
1083 // set up the stack frame
1084 image = vm->dataBase;
1085 #ifdef DEBUG_VM
1086 memData = (char*)image;
1087 #endif
1088
1089 programCounter = 0;
1090
1091 programStack -= 48;
1092
1093 *(int *)&image[ programStack + 44] = args[9];
1094 *(int *)&image[ programStack + 40] = args[8];
1095 *(int *)&image[ programStack + 36] = args[7];
1096 *(int *)&image[ programStack + 32] = args[6];
1097 *(int *)&image[ programStack + 28] = args[5];
1098 *(int *)&image[ programStack + 24] = args[4];
1099 *(int *)&image[ programStack + 20] = args[3];
1100 *(int *)&image[ programStack + 16] = args[2];
1101 *(int *)&image[ programStack + 12] = args[1];
1102 *(int *)&image[ programStack + 8 ] = args[0];
1103 *(int *)&image[ programStack + 4 ] = 0x77777777; // return stack
1104 *(int *)&image[ programStack ] = -1; // will terminate the loop on return
1105
1106 // off we go into generated code...
1107 entryPoint = getentrypoint(vm);
1108 opStack = &stack;
1109
1110 __asm__ __volatile__ (
1111 " movq %5,%%rsi \r\n" \
1112 " movl %4,%%edi \r\n" \
1113 " movq %2,%%r10 \r\n" \
1114 " movq %3,%%r8 \r\n" \
1115 " subq $24, %%rsp # fix alignment as call pushes one value \r\n" \
1116 " callq *%%r10 \r\n" \
1117 " addq $24, %%rsp \r\n" \
1118 " movl %%edi, %0 \r\n" \
1119 " movq %%rsi, %1 \r\n" \
1120 : "=m" (programStack), "=m" (opStack)
1121 : "m" (entryPoint), "m" (vm->dataBase), "m" (programStack), "m" (opStack)
1122 : "%rsi", "%rdi", "%rax", "%rbx", "%rcx", "%rdx", "%r8", "%r10", "%r15", "%xmm0"
1123 );
1124
1125 if ( opStack != &stack[1] ) {
1126 Com_Error( ERR_DROP, "opStack corrupted in compiled code (offset %ld)\n", (long int) ((void *) &stack[1] - opStack));
1127 }
1128 if ( programStack != stackOnEntry - 48 ) {
1129 Com_Error( ERR_DROP, "programStack corrupted in compiled code\n" );
1130 }
1131
1132 // Com_Printf("exiting %s level %d\n", vm->name, vm->callLevel);
1133 vm->programStack = stackOnEntry;
1134
1135 return *(int *)opStack;
1136 }
1137