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