From 2158389bef78ab2cb74d56ed37e0d9a9b5f2218d Mon Sep 17 00:00:00 2001 From: Spoike Date: Sat, 29 Aug 2009 14:56:42 +0000 Subject: [PATCH] Added IF_F instructions. Added -fassumeint so constants are by default ints instead of floats (like in C). Use a decimal point and it'll be a float. Fixed a couple of omissions/bugs reported by Blub. Added lame x86 jit which is still buggy, incomplete, and disabled. And bigfoot will hate it. git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@3349 fc73d0e0-1445-4013-8a0c-d673dee63da5 --- engine/qclib/comprout.c | 4 +- engine/qclib/execloop.h | 24 +- engine/qclib/initlib.c | 4 + engine/qclib/pr_comp.h | 9 +- engine/qclib/pr_exec.c | 56 +- engine/qclib/pr_multi.c | 7 +- engine/qclib/pr_x86.c | 1039 ++++++++++++++++++++++++++++++++++++ engine/qclib/progsint.h | 8 +- engine/qclib/qcc.h | 2 + engine/qclib/qcc_cmdlib.c | 2 +- engine/qclib/qcc_pr_comp.c | 213 ++++++-- engine/qclib/qcc_pr_lex.c | 20 +- engine/qclib/qccmain.c | 10 +- engine/qclib/test.c | 39 +- 14 files changed, 1341 insertions(+), 96 deletions(-) create mode 100644 engine/qclib/pr_x86.c diff --git a/engine/qclib/comprout.c b/engine/qclib/comprout.c index cb2a3c87..e42d7209 100644 --- a/engine/qclib/comprout.c +++ b/engine/qclib/comprout.c @@ -132,7 +132,7 @@ int Comp_Continue(progfuncs_t *progfuncs) if (setjmp(qcccompileerror)) { PostCompile(); - if (*errorfile) + if (*errorfile && externs->useeditor) externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); return false; } @@ -143,7 +143,7 @@ int Comp_Continue(progfuncs_t *progfuncs) { PostCompile(); - if (*errorfile) + if (*errorfile && externs->useeditor) externs->useeditor(progfuncs, errorfile, errorline, comp_nump, comp_parms); return false; diff --git a/engine/qclib/execloop.h b/engine/qclib/execloop.h index 1a779114..ad40f0e5 100644 --- a/engine/qclib/execloop.h +++ b/engine/qclib/execloop.h @@ -271,14 +271,14 @@ reeval: case OP_STORE_FI: OPB->_int = (int)OPA->_float; break; - case OP_STORE_I: - OPB->_int = OPA->_int; - break; + case OP_STORE_F: case OP_STORE_ENT: case OP_STORE_FLD: // integers case OP_STORE_S: + case OP_STORE_I: case OP_STORE_FNC: // pointers + case OP_STORE_P: OPB->_int = OPA->_int; break; case OP_STORE_V: @@ -493,24 +493,36 @@ reeval: //================== - case OP_IFNOTS: + case OP_IFNOT_S: RUNAWAYCHECK(); if (!OPA->string || !PR_StringToNative(progfuncs, OPA->string)) st += (sofs)st->b - 1; // offset the s++ break; + case OP_IFNOT_F: + RUNAWAYCHECK(); + if (!OPA->_float) + st += (sofs)st->b - 1; // offset the s++ + break; + case OP_IFNOT: RUNAWAYCHECK(); if (!OPA->_int) st += (sofs)st->b - 1; // offset the s++ break; - case OP_IFS: + case OP_IF_S: RUNAWAYCHECK(); if (OPA->string && PR_StringToNative(progfuncs, OPA->string)) st += (sofs)st->b - 1; // offset the s++ break; - + + case OP_IF_F: + RUNAWAYCHECK(); + if (OPA->_int) + st += (sofs)st->b - 1; // offset the s++ + break; + case OP_IF: RUNAWAYCHECK(); if (OPA->_int) diff --git a/engine/qclib/initlib.c b/engine/qclib/initlib.c index f65a1c2b..ca44b781 100644 --- a/engine/qclib/initlib.c +++ b/engine/qclib/initlib.c @@ -109,6 +109,10 @@ void PR_Configure (progfuncs_t *progfuncs, int addressable_size, int max_progs) edictrun_t *e; // int a; +#ifdef QCJIT + prinst->usejit = true; +#endif + max_fields_size=0; fields_size = 0; progfuncs->stringtable = 0; diff --git a/engine/qclib/pr_comp.h b/engine/qclib/pr_comp.h index a472ba2b..13b93bce 100644 --- a/engine/qclib/pr_comp.h +++ b/engine/qclib/pr_comp.h @@ -207,8 +207,8 @@ enum { OP_EQ_I, OP_NE_I, - OP_IFNOTS, - OP_IFS, + OP_IFNOT_S, + OP_IF_S, OP_NOT_I, //130 @@ -229,7 +229,7 @@ enum { OP_LOADA_FNC, OP_LOADA_I, - OP_STORE_P, + OP_STORE_P, //152... erm.. wait... OP_LOAD_P, OP_LOADP_F, @@ -311,6 +311,9 @@ enum { OP_SWITCH_I,//hmm. OP_GLOAD_V, + OP_IF_F, + OP_IFNOT_F, + OP_NUMREALOPS, /* diff --git a/engine/qclib/pr_exec.c b/engine/qclib/pr_exec.c index 54381ab7..bdd2af95 100644 --- a/engine/qclib/pr_exec.c +++ b/engine/qclib/pr_exec.c @@ -408,28 +408,6 @@ char *COM_TrimString(char *str) return buffer; } -#ifdef _WIN32 -#if (_MSC_VER >= 1400) -//with MSVC 8, use MS extensions -#define snprintf linuxlike_snprintf_vc8 -int VARGS linuxlike_snprintf_vc8(char *buffer, int size, const char *format, ...); -#define vsnprintf(a, b, c, d) vsnprintf_s(a, b, _TRUNCATE, c, d) -#else -//msvc crap -#define snprintf linuxlike_snprintf -int VARGS linuxlike_snprintf(char *buffer, int size, const char *format, ...); -#define vsnprintf linuxlike_vsnprintf -int VARGS linuxlike_vsnprintf(char *buffer, int size, const char *format, va_list argptr); -#endif - -#ifdef _MSC_VER -//these are provided so we don't use them -//but mingw has some defines elsewhere and makes gcc moan -#define _vsnprintf unsafe_vsnprintf -#define _snprintf unsafe_snprintf -#endif -#endif - char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) { static char buf[256]; @@ -526,7 +504,14 @@ char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) fdef = ED_FindField (progfuncs, assignment); if (!fdef) { - snprintf(buf, sizeof(buf), "Can't find field %s\n", assignment); + int l,nl = strlen(assignment); + strcpy(buf, "Can't find field "); + l = strlen(buf); + if (nl > sizeof(buf)-l-2) + nl = sizeof(buf)-l-2; + memcpy(buf+l, assignment, nl); + assignment[l+nl+0] = '\n'; + assignment[l+nl+1] = 0; return buf; } *(int *)val = G_INT(fdef->ofs); @@ -552,8 +537,17 @@ char *EvaluateDebugString(progfuncs_t *progfuncs, char *key) func = ED_FindFunction (progfuncs, s, &i, progsnum); if (!func) { + int l,nl = strlen(s); + assignment[-1] = '='; - snprintf(buf, sizeof(buf), "Can't find function %s\n", s); + + strcpy(buf, "Can't find field "); + l = strlen(buf); + if (nl > sizeof(buf)-l-2) + nl = sizeof(buf)-l-2; + memcpy(buf+l, assignment, nl); + assignment[l+nl+0] = '\n'; + assignment[l+nl+1] = 0; return buf; } *(func_t *)val = (func - pr_progstate[i].functions) | (i<<24); @@ -839,11 +833,19 @@ void PR_ExecuteCode (progfuncs_t *progfuncs, int s) float *glob; - int fnum = pr_xfunction - pr_functions; - - runaway = 100000000; + int fnum; prinst->continuestatement = -1; +#ifdef QCJIT + if (prinst->usejit) + { + PR_EnterJIT(progfuncs, s); + return; + } +#endif + fnum = pr_xfunction - pr_functions; + + runaway = 100000000; #define PRBOUNDSCHECK #define RUNAWAYCHECK() \ diff --git a/engine/qclib/pr_multi.c b/engine/qclib/pr_multi.c index f4b18143..5a1ade75 100644 --- a/engine/qclib/pr_multi.c +++ b/engine/qclib/pr_multi.c @@ -90,8 +90,13 @@ progsnum_t PR_LoadProgs(progfuncs_t *progfuncs, char *s, int headercrc, builtin_ { current_progstate->builtins = builtins; current_progstate->numbuiltins = numbuiltins; + +#ifdef QCJIT + if (prinst->usejit) + prinst->usejit = PR_GenerateJit(progfuncs); +#endif if (oldtype>=0) - PR_SwitchProgs(progfuncs, oldtype); + PR_SwitchProgs(progfuncs, oldtype); return a; //we could load it. Yay! } if (oldtype!=-1) diff --git a/engine/qclib/pr_x86.c b/engine/qclib/pr_x86.c new file mode 100644 index 00000000..acd0a1f4 --- /dev/null +++ b/engine/qclib/pr_x86.c @@ -0,0 +1,1039 @@ +/* +when I say JIT, I mean load time, not execution time. + +notes: + qc jump offsets are all constants. we have no variable offset jumps (other than function calls/returns) + field remapping... fields are in place, and cannot be adjusted. if a field is not set to 0, its assumed to be a constant. + +optimisations: + none at the moment... + instructions need to be chained. stuff that writes to C should be cacheable, etc. maybe we don't even need to do the write to C + it should also be possible to fold in eq+ifnot, so none of this silly storeing of floats in equality tests + + eax - tmp + ebx - prinst->edicttable + ecx - tmp + edx - tmp + esi - + edi - tmp (because its preserved by subfunctions + ebp - + + to use gas to provide binary opcodes: + vim -N blob.s && as blob.s && objdump.exe -d a.out +*/ + +#define PROGSUSED +#include "progsint.h" + +#ifdef QCJIT + +static float ta, tb, nullfloat=0; + +unsigned int *statementjumps; //[MAX_STATEMENTS*2] +unsigned char **statementoffsets; //[MAX_STATEMENTS] +unsigned int numjumps; +unsigned char *code; +unsigned int codesize; +unsigned int jitstatements; + +void EmitByte(unsigned char byte) +{ + code[codesize++] = byte; +} +void Emit4Byte(unsigned int value) +{ + code[codesize++] = (value>> 0)&0xff; + code[codesize++] = (value>> 8)&0xff; + code[codesize++] = (value>>16)&0xff; + code[codesize++] = (value>>24)&0xff; +} +void EmitAdr(void *value) +{ + Emit4Byte((unsigned int)value); +} +void EmitFloat(float value) +{ + union {float f; unsigned int i;} u; + u.f = value; + Emit4Byte(u.i); +} +void Emit2Byte(unsigned short value) +{ + code[codesize++] = (value>> 0)&0xff; + code[codesize++] = (value>> 8)&0xff; +} + +void EmitFOffset(void *func, int bias) +{ + union {void *f; unsigned int i;} u; + u.f = func; + u.i -= (unsigned int)&code[codesize+bias]; + Emit4Byte(u.i); +} + +void Emit4ByteJump(int statementnum, int offset) +{ + statementjumps[numjumps++] = codesize; + statementjumps[numjumps++] = statementnum; + statementjumps[numjumps++] = offset; + + //the offset is filled in later + codesize += 4; +} + +void FixupJumps(void) +{ + unsigned int j; + unsigned char *codesrc; + unsigned char *codedst; + unsigned int offset; + + unsigned int v; + + for (j = 0; j < numjumps;) + { + v = statementjumps[j++]; + codesrc = &code[v]; + + v = statementjumps[j++]; + codedst = statementoffsets[v]; + + v = statementjumps[j++]; + offset = (int)(codedst - (codesrc-v)); //3rd term because the jump is relative to the instruction start, not the instruction's offset + + codesrc[0] = (offset>> 0)&0xff; + codesrc[1] = (offset>> 8)&0xff; + codesrc[2] = (offset>>16)&0xff; + codesrc[3] = (offset>>24)&0xff; + } +} + +int PR_LeaveFunction (progfuncs_t *progfuncs); +int PR_EnterFunction (progfuncs_t *progfuncs, dfunction_t *f, int progsnum); + +pbool PR_GenerateJit(progfuncs_t *progfuncs) +{ + unsigned int i; + dstatement16_t *op = (dstatement16_t*)current_progstate->statements; + unsigned int numstatements = current_progstate->progs->numstatements; + int *glob = (int*)current_progstate->globals; + + if (current_progstate->numbuiltins) + return; + + jitstatements = numstatements; + + statementjumps = malloc(numstatements*12); + statementoffsets = malloc(numstatements*4); + code = malloc(numstatements*500); + + numjumps = 0; + codesize = 0; + + + + for (i = 0; i < numstatements; i++) + { + statementoffsets[i] = &code[codesize]; + switch(op[i].op) + { + //jumps + case OP_IF: + //integer compare + //if a, goto b + + //cmpl $0,glob[A] + EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); + //jnz B + EmitByte(0x0f);EmitByte(0x85);Emit4ByteJump(i + (signed short)op[i].b, -4); + break; + + case OP_IFNOT: + //integer compare + //if !a, goto b + + //cmpl $0,glob[A] + EmitByte(0x83);EmitByte(0x3d);EmitAdr(glob + op[i].a);EmitByte(0x0); + //jz B + EmitByte(0x0f);EmitByte(0x84);Emit4ByteJump(i + (signed short)op[i].b, -4); + break; + + case OP_GOTO: + EmitByte(0xE9);Emit4ByteJump(i + (signed short)op[i].a, -4); + break; + + //function returns + case OP_DONE: + case OP_RETURN: + //done and return are the same + + //part 1: store A into OFS_RETURN + + if (!op[i].a) + { + //assumption: anything that returns address 0 is a void or zero return. + //thus clear eax and copy that to the return vector. + EmitByte(0x31);EmitByte(0xc0); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+0); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+1); + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+2); + } + else + { + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //movl glob[A+0],edx + EmitByte(0x8b);EmitByte(0x0d);EmitAdr(glob + op[i].a+1); + //movl glob[A+0],ecx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(glob + op[i].a+2); + //movl eax, glob[OFS_RET+0] + EmitByte(0xa3);EmitAdr(glob + OFS_RETURN+0); + //movl edx, glob[OFS_RET+0] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + OFS_RETURN+1); + //movl ecx, glob[OFS_RET+0] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + OFS_RETURN+2); + } + + //call leavefunction to get the return address + +// pushl progfuncs + EmitByte(0x68);EmitAdr(progfuncs); +// call PR_LeaveFunction + EmitByte(0xe8);EmitFOffset(PR_LeaveFunction, 4); +// add $4,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0x04); +// movl pr_depth,%edx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(&pr_depth); +// cmp prinst->exitdepth,%edx + EmitByte(0x3b);EmitByte(0x15);EmitAdr(&prinst->exitdepth); +// je returntoc + EmitByte(0x74);EmitByte(0x09); +// mov statementoffsets[%eax*4],%eax + EmitByte(0x8b);EmitByte(0x04);EmitByte(0x85);EmitAdr(statementoffsets+1); +// jmp eax + EmitByte(0xff);EmitByte(0xe0); +// returntoc: +// ret + EmitByte(0xc3); + break; + + //function calls + case OP_CALL0: + case OP_CALL1: + case OP_CALL2: + case OP_CALL3: + case OP_CALL4: + case OP_CALL5: + case OP_CALL6: + case OP_CALL7: + case OP_CALL8: + //save the state in place the rest of the engine can cope with + //movl $i, pr_xstatement + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&pr_xstatement);Emit4Byte(i); + //movl $(op[i].op-OP_CALL0), pr_argc + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&pr_argc);Emit4Byte(op[i].op-OP_CALL0); + + //figure out who we're calling, and what that involves + //%eax = glob[A] + EmitByte(0xa1); EmitAdr(glob + op[i].a); + //eax is now the func num + + //mov %eax,%ecx + EmitByte(0x89); EmitByte(0xc1); + //shr $24,%ecx + EmitByte(0xc1); EmitByte(0xe9); EmitByte(0x18); + //ecx is now the progs num for the new func + + //cmp %ecx,pr_typecurrent + EmitByte(0x39); EmitByte(0x0d); EmitAdr(&pr_typecurrent); + //je sameprogs + EmitByte(0x74); EmitByte(0x3); + { + //can't handle switching progs + + //FIXME: recurse though PR_ExecuteProgram + //push eax + //push progfuncs + //call PR_ExecuteProgram + //add $8,%esp + //remember to change the je above + + //err... exit depth? no idea + EmitByte(0xcd);EmitByte(op[i].op); + + + //ret + EmitByte(0xc3); + } + //sameprogs: + + //andl $0x00ffffff, %eax + EmitByte(0x25);Emit4Byte(0x00ffffff); + + //mov $sizeof(dfunction_t),%edx + EmitByte(0xba);Emit4Byte(sizeof(dfunction_t)); + //mul %edx + EmitByte(0xf7); EmitByte(0xe2); + //add pr_functions,%eax + EmitByte(0x05); EmitAdr(pr_functions); + + //eax is now the dfunction_t to be called + //edx is clobbered. + + //mov (%eax),%edx + EmitByte(0x8b);EmitByte(0x10); + //edx is now the first statement number + //cmp $0,%edx + EmitByte(0x83);EmitByte(0xfa);EmitByte(0x00); + //jl isabuiltin + EmitByte(0x7c);EmitByte(22); + + { + //push %ecx + EmitByte(0x51); + //push %eax + EmitByte(0x50); + //pushl progfuncs + EmitByte(0x68);EmitAdr(progfuncs); + //call PR_EnterFunction + EmitByte(0xe8);EmitFOffset(PR_EnterFunction, 4); + //sub $12,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0xc); + //eax is now the next statement number (first of the new function, usually equal to ecx, but not always) + + //jmp statementoffsets[%eax*4] + EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(statementoffsets+1); + } + //isabuiltin: + + + //push current_progstate->globals + EmitByte(0x68);EmitAdr(current_progstate->globals); + //push progfuncs + EmitByte(0x68);EmitAdr(progfuncs); + //neg %edx + EmitByte(0xf7);EmitByte(0xda); + //call externs->globalbuiltins[%edx,4] +//FIXME: make sure this dereferences + EmitByte(0xff);EmitByte(0x14);EmitByte(0x95);EmitAdr(externs->globalbuiltins); + //add $8,%esp + EmitByte(0x83);EmitByte(0xc4);EmitByte(0x8); + + //but that builtin might have been Abort() + + //mov prinst->continuestatement,%eax + EmitByte(0xa1);EmitAdr(&prinst->continuestatement); + //eax is now prinst->continuestatement + + //cmp $-1,%eax + EmitByte(0x83);EmitByte(0xf8);EmitByte(0xff); + //je donebuiltincall + EmitByte(0x74);EmitByte(10+8); + { +EmitByte(0xcc); + //jmp statementoffsets[%eax*4] + EmitByte(0xff);EmitByte(0x24);EmitByte(0x85);EmitAdr(statementoffsets+1); + + //mov $-1,prinst->continuestatement + EmitByte(0xc7);EmitByte(0x05);EmitAdr(&prinst->continuestatement+1);Emit4Byte((unsigned int)-1); + } + //donebuiltincall: + break; + + case OP_MUL_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_DIV_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fdivs glob[B] + EmitByte(0xd8);EmitByte(0x35);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_ADD_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + case OP_SUB_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_NOT_F: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //fldz + EmitByte(0xd9);EmitByte(0xee); + //fnstsw %ax + EmitByte(0xdf);EmitByte(0xe0); + //testb 0x40,%ah + EmitByte(0xf6);EmitByte(0xc4);EmitByte(0x40); + //je noteq + EmitByte(0x74);EmitByte(0x0c); + //movl 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05);EmitAdr(glob + op[i].c);EmitFloat(0.0f); + //jmp end + EmitByte(0xeb);EmitByte(0x0a); + //noteq: + //movl 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05);EmitAdr(glob + op[i].c);EmitFloat(1.0f); + //end: + break; + + case OP_STORE_F: + case OP_STORE_S: + case OP_STORE_ENT: + case OP_STORE_FLD: + case OP_STORE_FNC: + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //movl eax,glob[B] + EmitByte(0xa3);EmitAdr(glob + op[i].b); + break; + + case OP_STORE_V: + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //movl glob[A+1],edx + EmitByte(0x8b);EmitByte(0x0d);EmitAdr(glob + op[i].a+1); + //movl glob[A+2],ecx + EmitByte(0x8b);EmitByte(0x15);EmitAdr(glob + op[i].a+2); + + //movl eax, glob[B+0] + EmitByte(0xa3);EmitAdr(glob + op[i].b+0); + //movl edx, glob[B+1] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + op[i].b+1); + //movl ecx, glob[B+2] + EmitByte(0x89);EmitByte(0x15);EmitAdr(glob + op[i].b+2); + break; + + case OP_LOAD_F: + case OP_LOAD_S: + case OP_LOAD_ENT: + case OP_LOAD_FLD: + case OP_LOAD_FNC: + case OP_LOAD_V: + //a is the ent number, b is the field + //c is the dest + + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //FIXME: bound eax (ent number) + //FIXME: bound ecx (field index) + //mov (ebx,eax,4).%eax + EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); + //eax is now an edictrun_t + //mov fields(,%eax,4),%edx + EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); + //edx is now the field array for that ent + + //mov fieldajust(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); + //mov edx,glob[C] + EmitByte(0xa3);EmitAdr(glob + op[i].c); + + if (op[i].op == OP_LOAD_V) + { + //mov fieldajust+4(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->fieldadjust*4); + //mov edx,glob[C+1] + EmitByte(0xa3);EmitAdr(glob + op[i].c+1); + + //mov fieldajust+8(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + EmitByte(0x8b); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(4+progfuncs->fieldadjust*4); + //mov edx,glob[C+1] + EmitByte(0xa3);EmitAdr(glob + op[i].c+2); + } + break; + + case OP_ADDRESS: + //a is the ent number, b is the field + //c is the dest + + //movl glob[A+0],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //FIXME: bound eax (ent number) + //FIXME: bound ecx (field index) + //mov (ebx,eax,4).%eax + EmitByte(0x8b); EmitByte(0x04); EmitByte(0x83); + //eax is now an edictrun_t + //mov fields(,%eax,4),%edx + EmitByte(0x8b);EmitByte(0x50);EmitByte((int)&((edictrun_t*)NULL)->fields); + //edx is now the field array for that ent + //mov fieldajust(%edx,%ecx,4),%eax //offset = progfuncs->fieldadjust + //EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); EmitByte(progfuncs->fieldadjust*4); + EmitByte(0x8d); EmitByte(0x84); EmitByte(0x8a); Emit4Byte(progfuncs->fieldadjust*4); + //mov edx,glob[C] + EmitByte(0xa3);EmitAdr(glob + op[i].c); + break; + + case OP_STOREP_F: + case OP_STOREP_S: + case OP_STOREP_ENT: + case OP_STOREP_FLD: + case OP_STOREP_FNC: + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //mov %eax,(%ecx) + EmitByte(0x89);EmitByte(0x01); + break; + + case OP_STOREP_V: + //mov glob[B],ecx + EmitByte(0x8b); EmitByte(0x0d);EmitAdr(glob + op[i].b); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,0(%ecx) + EmitByte(0x89);EmitByte(0x01); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,4(%ecx) + EmitByte(0x89);EmitByte(0x41);EmitByte(0x04); + //movl glob[A],eax + EmitByte(0xa1);EmitAdr(glob + op[i].a+0); + //mov %eax,8(%ecx) + EmitByte(0x89);EmitByte(0x41);EmitByte(0x08); + break; + + case OP_EQ_E: + case OP_EQ_FNC: + //integer equality + //movl glob[A],%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp glob[B],%eax + EmitByte(0x3b); EmitByte(0x0f); EmitAdr(glob + op[i].b); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(1.0f); + break; + + case OP_NE_E: + case OP_NE_FNC: + //integer equality + //movl glob[A],%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp glob[B],%eax + EmitByte(0x3b); EmitByte(0x0f); EmitAdr(glob + op[i].b); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(1.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + break; + + case OP_NOT_ENT: + case OP_NOT_FNC: + //cmp glob[B],%eax + EmitByte(0x8c); EmitByte(0x3d); EmitAdr(glob + op[i].a);EmitByte(0x00); + //je 12 + EmitByte(0x74);EmitByte(0x0c); + //mov 0.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(0.0f); + //jmp 10 + EmitByte(0xeb);EmitByte(0x0a); + //mov 1.0f,glob[C] + EmitByte(0xc7);EmitByte(0x05); EmitAdr(glob + op[i].a);EmitFloat(1.0f); + break; + + case OP_BITOR: //floats... + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); + //fistp tb + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&tb); + //fistp ta + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&ta); + //mov ta,%eax + EmitByte(0xa1); EmitAdr(&ta); + //and tb,%eax + EmitByte(0x09); EmitByte(0x05);EmitAdr(&tb); + //fild tb + EmitByte(0xdf); EmitByte(0x05);EmitAdr(&tb); + //fstps glob[C] + EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_BITAND: + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05);EmitAdr(glob + op[i].b); + //fistp tb + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&tb); + //fistp ta + EmitByte(0xdf); EmitByte(0x1d);EmitAdr(&ta); + //mov ta,%eax + EmitByte(0xa1); EmitAdr(&ta); + //and tb,%eax + EmitByte(0x21); EmitByte(0x05);EmitAdr(&tb); + //fild tb + EmitByte(0xdf); EmitByte(0x05);EmitAdr(&tb); + //fstps glob[C] + EmitByte(0xd9); EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_AND: + //test floats properly, so we don't get confused with -0.0 + + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onefalse + EmitByte(0x75); EmitByte(0x1f); + + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //jne onefalse + EmitByte(0x75); EmitByte(0x0c); + + //mov float0,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); + + //onefalse: + //mov float1,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //done: + break; + case OP_OR: + //test floats properly, so we don't get confused with -0.0 + + //flds glob[A] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].a); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onetrue + EmitByte(0x74); EmitByte(0x1f); + + //flds glob[B] + EmitByte(0xd9); EmitByte(0x05); EmitAdr(glob + op[i].b); + //fcomps nullfloat + EmitByte(0xd8); EmitByte(0x1d); EmitAdr(&nullfloat); + //fnstsw %ax + EmitByte(0xdf); EmitByte(0xe0); + //test $0x40,%ah + EmitByte(0xf6); EmitByte(0xc4);EmitByte(0x40); + //je onetrue + EmitByte(0x74); EmitByte(0x0c); + + //mov float0,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); + + //onetrue: + //mov float1,glob[C] + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); + //done: + break; + + case OP_EQ_S: + case OP_NE_S: + //put a in ecx + //put b in edi + //mov a,%ecx + EmitByte(0x8b); EmitByte(0x0d); EmitAdr(glob + op[i].a); + //mov b,%edi + EmitByte(0x8b); EmitByte(0x3d); EmitAdr(glob + op[i].b); + + //early out if they're equal + //cmp %ecx,%edi + EmitByte(0x39); EmitByte(0xd1); + //je _true + EmitByte(0x74); EmitByte(0x68); + + //if a is 0, check if b is "" + //jecxz ais0 + EmitByte(0xe3); EmitByte(0x1a); + + //if b is 0, check if a is "" + //cmp $0,%edi + EmitByte(0x83); EmitByte(0xff); EmitByte(0x00); + //jne bnot0 + EmitByte(0x75); EmitByte(0x2a); + { + //push a + EmitByte(0x51); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x4b); + //jmp _false + EmitByte(0xeb); EmitByte(0x3d); + + //ais0: + { + //push edi + EmitByte(0x57); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x36); + //jmp _false + EmitByte(0xeb); EmitByte(0x28); + } + } + //bnot0: + + //push ecx + EmitByte(0x51); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //push %eax + EmitByte(0x50); + + //push %edi + EmitByte(0x57); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + + + //push %eax + EmitByte(0x50); + //call strcmp + EmitByte(0xe8); EmitFOffset(strcmp,4); + //add $16,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x10); + //cmp $0,%eax + EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat((op[i].op == OP_NE_S)?1.0f:0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat((op[i].op == OP_NE_S)?0.0f:1.0f); +//_done: + break; + + case OP_NOT_S: + //mov A,%eax + EmitByte(0xa1);EmitAdr(glob + op[i].a); + //cmp $0,%eax + EmitByte(0x83); EmitByte(0xf8); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x1f); + //push %eax + EmitByte(0x50); + //push progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call PR_StringToNative + EmitByte(0xe8); EmitFOffset(PR_StringToNative,4); + //add $8,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x08); + //cmpb $0,(%eax) + EmitByte(0x80); EmitByte(0x38); EmitByte(0x00); + //je _true + EmitByte(0x74); EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); +//_done: + break; + + case OP_ADD_V: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+0); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+1); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fadds glob[B] + EmitByte(0xd8);EmitByte(0x05);EmitAdr(glob + op[i].b+2); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + break; + case OP_SUB_V: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+0); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+1); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fsubs glob[B] + EmitByte(0xd8);EmitByte(0x25);EmitAdr(glob + op[i].b+2); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + break; + + case OP_MUL_V: + //this is actually a dotproduct + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+0); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+0); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+1); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+1); + + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a+2); + //fmuls glob[B] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + op[i].b+2); + + //faddp + EmitByte(0xde);EmitByte(0xc1); + //faddp + EmitByte(0xde);EmitByte(0xc1); + + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c); + break; + + case OP_EQ_F: + case OP_NE_F: + case OP_LE: + case OP_GE: + case OP_LT: + case OP_GT: + //flds glob[A] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].a); + //flds glob[B] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + op[i].b); + //fcomip %st(1),%st + EmitByte(0xdf);EmitByte(0xe9); + //fstp %st(0) (aka: pop) + EmitByte(0xdd);EmitByte(0xd8); + + //jcc _true + if (op[i].op == OP_LE) + EmitByte(0x7e); //jle + else if (op[i].op == OP_GE) + EmitByte(0x7d); //jge + else if (op[i].op == OP_LT) + EmitByte(0x7c); //jl + else if (op[i].op == OP_GT) + EmitByte(0x7f); //jg + else if (op[i].op == OP_NE_F) + EmitByte(0x75); //jne + else + EmitByte(0x74); //je + EmitByte(0x0c); +//_false: + //mov 0.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(0.0f); + //jmp done + EmitByte(0xeb); EmitByte(0x0a); +//_true: + //mov 1.0f,c + EmitByte(0xc7); EmitByte(0x05); EmitAdr(glob + op[i].c); EmitFloat(1.0f); +//_done: + break; + + case OP_MUL_FV: + case OP_MUL_VF: + // + { + int v; + int f; + if (op[i].op == OP_MUL_FV) + { + f = op[i].a; + v = op[i].b; + } + else + { + v = op[i].a; + f = op[i].b; + } + + //flds glob[F] + EmitByte(0xd9);EmitByte(0x05);EmitAdr(glob + f); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+0); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+0); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+1); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+1); + + //flds glob[V0] + EmitByte(0xd8);EmitByte(0x0d);EmitAdr(glob + v+2); + //fmul st(1) + EmitByte(0xd8);EmitByte(0xc9); + //fstps glob[C] + EmitByte(0xd9);EmitByte(0x1d);EmitAdr(glob + op[i].c+2); + + //fstp %st(0) (aka: pop) + EmitByte(0xdd);EmitByte(0xd8); + } + break; + + case OP_STATE: + //externs->stateop(progfuncs, OPA->_float, OPB->function); + //push b + EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].b); + //push a + EmitByte(0xff);EmitByte(0x35);EmitAdr(glob + op[i].a); + //push $progfuncs + EmitByte(0x68); EmitAdr(progfuncs); + //call externs->stateop + EmitByte(0xe8); EmitFOffset(externs->stateop, 4); + //add $12,%esp + EmitByte(0x83); EmitByte(0xc4); EmitByte(0x0c); + break; +/* + case OP_EQ_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; + + case OP_NE_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; + + case OP_NOT_V: + EmitByte(0xcd);EmitByte(op[i].op); + printf("QCJIT: instruction %i is not implemented\n", op[i].op); + break; +*/ + default: + printf("QCJIT: Extended instruction set %i is not supported, not using jit.\n", op[i].op); + + + free(statementjumps); //[MAX_STATEMENTS] + free(statementoffsets); //[MAX_STATEMENTS] + free(code); + statementoffsets = NULL; + return false; + } + } + + FixupJumps(); + +#ifdef _WIN32 + { + DWORD old; + + //this memory is on the heap. + //this means that we must maintain read/write protection, or libc will crash us + VirtualProtect(code, codesize, PAGE_EXECUTE_READWRITE, &old); + } +#endif + +// externs->WriteFile("jit.x86", code, codesize); + + return true; +} + +void PR_EnterJIT(progfuncs_t *progfuncs, int statement) +{ +#ifdef __GNUC__ + //call, it clobbers pretty much everything. + asm("call %0" :: "r"(statementoffsets[statement+1]),"b"(prinst->edicttable):"cc","memory","eax","ecx","edx"); +#elif defined(_MSC_VER) + void *entry = statementoffsets[statement+1]; + void *edicttable = prinst->edicttable; + __asm { + pushad + mov eax,entry + mov ebx,edicttable + call eax + popad + } +#else + #error "Sorry, no idea how to enter assembler safely for your compiler" +#endif +} +#endif \ No newline at end of file diff --git a/engine/qclib/progsint.h b/engine/qclib/progsint.h index d03bfe12..0e67cbf5 100644 --- a/engine/qclib/progsint.h +++ b/engine/qclib/progsint.h @@ -28,6 +28,10 @@ typedef unsigned char qbyte; #include +#if defined(_M_IX86) || defined(__i386__) +#define QCJIT +#endif + #define DLL_PROG #ifndef PROGSUSED #define PROGSUSED @@ -327,7 +331,9 @@ void PR_SetBuiltins(int type); #define vars(type, name, size) type name[size] typedef struct prinst_s { - +#ifdef QCJIT + pbool usejit; +#endif char **tempstrings; int maxtempstrings; int numtempstrings; diff --git a/engine/qclib/qcc.h b/engine/qclib/qcc.h index db5c4b8f..75b2b378 100644 --- a/engine/qclib/qcc.h +++ b/engine/qclib/qcc.h @@ -470,11 +470,13 @@ extern pbool keywords_coexist; extern pbool output_parms; extern pbool autoprototype; extern pbool flag_ifstring; +extern pbool flag_iffloat; extern pbool flag_acc; extern pbool flag_caseinsensative; extern pbool flag_laxcasts; extern pbool flag_hashonly; extern pbool flag_fasttrackarrays; +extern pbool flag_assume_integer; extern pbool opt_overlaptemps; extern pbool opt_shortenifnots; diff --git a/engine/qclib/qcc_cmdlib.c b/engine/qclib/qcc_cmdlib.c index 7766b94c..d2f8d845 100644 --- a/engine/qclib/qcc_cmdlib.c +++ b/engine/qclib/qcc_cmdlib.c @@ -24,7 +24,7 @@ const unsigned int type_size[12] = {1, //void 1, //entity 1, //field sizeof(func_t)/4,//function - sizeof(void *)/4,//pointer + 1, //pointer (its an int index) 1, //integer 1, //fixme: how big should a variant be? 0, //ev_struct. variable sized. diff --git a/engine/qclib/qcc_pr_comp.c b/engine/qclib/qcc_pr_comp.c index d22f4a5f..bc53ef69 100644 --- a/engine/qclib/qcc_pr_comp.c +++ b/engine/qclib/qcc_pr_comp.c @@ -68,11 +68,13 @@ pbool output_parms; //emit some PARMX fields. confuses decompilers. pbool autoprototype; //take two passes over the source code. First time round doesn't enter and functions or initialise variables. pbool pr_subscopedlocals; //causes locals to be valid ONLY within their statement block. (they simply can't be referenced by name outside of it) pbool flag_ifstring; //makes if (blah) equivelent to if (blah != "") which resolves some issues in multiprogs situations. +pbool flag_iffloat; //use an op_if_f instruction instead of op_if so if(-0) evaluates to false. pbool flag_acc; //reacc like behaviour of src files (finds *.qc in start dir and compiles all in alphabetical order) pbool flag_caseinsensative; //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod pbool flag_laxcasts; //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. pbool flag_hashonly; //Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile pbool flag_fasttrackarrays; //Faster arrays, dynamically detected, activated only in supporting engines. +pbool flag_assume_integer; //5 - is that an integer or a float? qcc says float. but we support int too, so maybe we want that instead? pbool opt_overlaptemps; //reduce numpr_globals by reuse of temps. When they are not needed they are freed for reuse. The way this is implemented is better than frikqcc's. (This is the single most important optimisation) pbool opt_assignments; //STORE_F isn't used if an operation wrote to a temp. @@ -456,12 +458,12 @@ QCC_opcode_t pr_opcodes[] = {7, "&", "BITAND_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, {7, "|", "BITOR_FI", 5, ASSOC_LEFT, &type_float, &type_integer, &type_float}, -{7, "&&", "AND_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, -{7, "||", "OR_I", 5, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, -{7, "&&", "AND_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "||", "OR_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, -{7, "&&", "AND_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_float}, -{7, "||", "OR_FI", 5, ASSOC_LEFT, &type_float, &type_float, &type_integer}, +{7, "&&", "AND_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "||", "OR_I", 7, ASSOC_LEFT, &type_integer, &type_integer, &type_integer}, +{7, "&&", "AND_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "||", "OR_IF", 7, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, +{7, "&&", "AND_FI", 7, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{7, "||", "OR_FI", 7, ASSOC_LEFT, &type_float, &type_float, &type_integer}, {7, "!=", "NE_IF", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, {7, "!=", "NE_FI", 5, ASSOC_LEFT, &type_integer, &type_float, &type_integer}, @@ -496,6 +498,9 @@ QCC_opcode_t pr_opcodes[] = {7, "", "SWITCH_I", -1, ASSOC_LEFT, &type_void, NULL, &type_void}, {7, "<>", "GLOAD_S", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, +{6, "", "IF_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, +{6, "","IFNOT_F", -1, ASSOC_RIGHT, &type_float, NULL, &type_void}, + /* emulated ops begin here */ {7, "<>", "OP_EMULATED", -1, ASSOC_LEFT, &type_float, &type_float, &type_float}, @@ -571,16 +576,17 @@ pbool OpAssignsToB(unsigned int op) return true; return false; } -pbool OpAssignedTo(QCC_def_t *v, unsigned int op) +/*pbool OpAssignedTo(QCC_def_t *v, unsigned int op) { if(OpAssignsToC(op)) { - } else if(OpAssignsToB(op)) + } + else if(OpAssignsToB(op)) { } return false; } - +*/ #undef ASSOC_RIGHT_RESULT #define TOP_PRIORITY 7 @@ -798,7 +804,13 @@ QCC_opcode_t *opcodeprioritized[TOP_PRIORITY+1][128] = NULL }, { //7 &pr_opcodes[OP_AND], + &pr_opcodes[OP_AND_I], + &pr_opcodes[OP_AND_IF], + &pr_opcodes[OP_AND_FI], &pr_opcodes[OP_OR], + &pr_opcodes[OP_OR_I], + &pr_opcodes[OP_OR_IF], + &pr_opcodes[OP_OR_FI], NULL } }; @@ -895,8 +907,10 @@ pbool QCC_OPCodeValid(QCC_opcode_t *op) //stores into a pointer (generated from 'ent.field=XXX') case OP_STOREP_I: //no worse than the other OP_STOREP_X functions + case OP_STOREP_P: //reads from an entity field case OP_LOAD_I: //no worse than the other OP_LOAD_X functions. + case OP_LOAD_P: return true; //stores into the globals array. @@ -998,17 +1012,22 @@ pbool QCC_OPCodeValid(QCC_opcode_t *op) case OP_DIVSTOREP_F: case OP_STORE_IF: case OP_STORE_FI: + case OP_STORE_P: case OP_STOREP_IF: // store a value to a pointer case OP_STOREP_FI: - case OP_IFNOTS: - case OP_IFS: + case OP_IFNOT_S: + case OP_IF_S: return true; + + case OP_IFNOT_F: //added, but not in dp yet + case OP_IF_F: + return false; case OP_CP_ITOF: case OP_CP_FTOI: return false; //DPFIXME: These are not bounds checked at all. case OP_GLOBALADDRESS: - return false; //DPFIXME: DP will reject these pointers if they are ever used. + return true; //DPFIXME: DP will reject these pointers if they are ever used. case OP_POINTER_ADD: return true; //just maths. @@ -1886,8 +1905,10 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var if ((var_a->constant && var_b->constant && !var_a->temp && !var_b->temp) || var_a->ofs == var_b->ofs) QCC_PR_ParseWarning(WARN_CONSTANTCOMPARISON, "Result of comparison is constant"); break; - case OP_IFS: - case OP_IFNOTS: + case OP_IF_S: + case OP_IFNOT_S: + case OP_IF_F: + case OP_IFNOT_F: case OP_IF: case OP_IFNOT: // if (var_a->type->type == ev_function && !var_a->temp) @@ -1908,7 +1929,10 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var if (statements[numstatements-1].c == var_a->ofs) { static QCC_def_t nvara; - op = &pr_opcodes[OP_IF]; + if (statements[numstatements-1].op == OP_NOT_F) + op = &pr_opcodes[OP_IF_F]; + else + op = &pr_opcodes[OP_IF]; numstatements--; QCC_FreeTemp(var_a); memcpy(&nvara, var_a, sizeof(nvara)); @@ -1919,14 +1943,32 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var } } } - else if (op - pr_opcodes == OP_IFNOTS) + else if (op - pr_opcodes == OP_IFNOT_F) + { + if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_F) + { + if (statements[numstatements-1].c == var_a->ofs) + { + static QCC_def_t nvara; + op = &pr_opcodes[OP_IF_F]; + numstatements--; + QCC_FreeTemp(var_a); + memcpy(&nvara, var_a, sizeof(nvara)); + nvara.ofs = statements[numstatements].a; + var_a = &nvara; + + optres_shortenifnots++; + } + } + } + else if (op - pr_opcodes == OP_IFNOT_S) { if (opt_shortenifnots && var_a && statements[numstatements-1].op == OP_NOT_S) { if (statements[numstatements-1].c == var_a->ofs) { static QCC_def_t nvara; - op = &pr_opcodes[OP_IFS]; + op = &pr_opcodes[OP_IF_S]; numstatements--; QCC_FreeTemp(var_a); memcpy(&nvara, var_a, sizeof(nvara)); @@ -1937,7 +1979,7 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var } } } - else if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6)) + else if (((unsigned) ((op - pr_opcodes) - OP_STORE_F) < 6) || (op-pr_opcodes) == OP_STORE_P) { // remove assignments if what should be assigned is the 3rd operand of the previous statement? // don't if it's a call, callH, switch or case @@ -1977,7 +2019,7 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var { switch(op - pr_opcodes) { - case OP_IFS: + case OP_IF_S: var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1, false); numstatements--; var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); @@ -1988,7 +2030,7 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var op = &pr_opcodes[OP_IF]; break; - case OP_IFNOTS: + case OP_IFNOT_S: var_c = QCC_PR_GetDef(type_string, "string_null", NULL, true, 1, false); numstatements--; var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_S], var_a, var_c, NULL); @@ -1999,6 +2041,28 @@ QCC_def_t *QCC_PR_Statement ( QCC_opcode_t *op, QCC_def_t *var_a, QCC_def_t *var op = &pr_opcodes[OP_IFNOT]; break; + case OP_IF_F: + var_c = QCC_MakeFloatDef(0); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IF]; + break; + + case OP_IFNOT_F: + var_c = QCC_MakeFloatDef(0); + numstatements--; + var_a = QCC_PR_Statement(&pr_opcodes[OP_NE_F], var_a, var_c, NULL); + statement = &statements[numstatements]; + numstatements++; + + QCC_FreeTemp(var_a); + op = &pr_opcodes[OP_IFNOT]; + break; + case OP_ADDSTORE_F: op = &pr_opcodes[OP_ADD_F]; var_c = var_b; @@ -4174,10 +4238,44 @@ reloop: nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_FNC], d, ao, NULL); //get pointer to precise def. nd->type = d->type; break; + case ev_pointer: + if (ao->constant && !G_INT(ao->ofs)) + ao->ofs = 0; + if (d->arraysize>1) //use the array + { + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + } + else + { //dereference the pointer. + switch(newtype->aux_type->type) + { + case ev_pointer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_float: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + case ev_integer: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, ao, NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; + default: + QCC_PR_ParseError(ERR_NOVALIDOPCODES, "No op available. Try assembler"); + nd = NULL; + break; + } + } + break; case ev_integer: nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. break; - case ev_struct: nd = QCC_PR_Statement(&pr_opcodes[OP_LOADA_I], d, ao, NULL); //get pointer to precise def. nd->type = d->type; @@ -4302,6 +4400,10 @@ reloop: nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_F], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. nd->type = d->type->aux_type; break; + case ev_vector: + nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_V], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. + nd->type = d->type->aux_type; + break; case ev_integer: nd = QCC_PR_Statement(&pr_opcodes[OP_LOADP_I], d, QCC_PR_Statement (&pr_opcodes[OP_CONV_FTOI], ao, 0, NULL), NULL); //get pointer to precise def. nd->type = d->type->aux_type; @@ -4695,7 +4797,6 @@ QCC_def_t *QCC_PR_Term (void) if ((unsigned)(statements[numstatements-1].op - OP_LOAD_F) < 6 || statements[numstatements-1].op == OP_LOAD_I || statements[numstatements-1].op == OP_LOAD_P) { statements[numstatements-1].op = OP_ADDRESS; - QCC_PR_ParseWarning(0, "debug: &ent.field"); e->type = QCC_PR_PointerType(e->type); return e; } @@ -5011,9 +5112,30 @@ QCC_def_t *QCC_PR_Expression (int priority, int exprflags) type_pointer->aux_type->type = e->type->type; e->type = type_pointer; } - if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7) + if ( !simplestore && (unsigned)(statements[numstatements-1].op - OP_LOADP_F) < 7 && statements[numstatements-1].c == e->ofs) { - statements[numstatements-1].op = OP_ADD_I; + if (!statements[numstatements-1].b) + { + //if the loadp has no offset, remove the instruction and convert the dest of this instruction directly to the pointer's load address + //this kills the add 0. + e->ofs = statements[numstatements-1].a; + numstatements--; + + if (e->type->type != ev_pointer) + { + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + } + else + { + statements[numstatements-1].op = OP_ADD_I; + if (e->type->type != ev_pointer) + { + type_pointer->aux_type->type = e->type->type; + e->type = type_pointer; + } + } } if ( !simplestore && statements[numstatements-1].op == OP_LOADP_C && e->ofs == statements[numstatements-1].c) { @@ -5478,9 +5600,11 @@ void QCC_PR_ParseStatement (void) } else if (!typecmp( e->type, type_string) && flag_ifstring) //special case, as strings are now pointers, not offsets from string table { - QCC_PR_ParseWarning(0, "while (string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOTS], e, 0, &patch1)); + QCC_PR_ParseWarning(WARN_IFSTRING_USED, "while(string) can result in bizzare behaviour"); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); } + else if (!typecmp( e->type, type_float) && flag_iffloat) //special case, as negative 0 is also zero + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); else QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); } @@ -5628,8 +5752,10 @@ void QCC_PR_ParseStatement (void) if (!typecmp( e->type, type_string) && flag_ifstring) { QCC_PR_ParseWarning(WARN_IFSTRING_USED, "do {} while(string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFS], e, NULL, &patch2)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, NULL, &patch2)); } + else if (!typecmp( e->type, type_float) && flag_iffloat) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, NULL, &patch2)); else QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, NULL, &patch2)); @@ -5724,8 +5850,10 @@ void QCC_PR_ParseStatement (void) if (!typecmp( e->type, type_string) && flag_ifstring) { QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if not(string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFS], e, 0, &patch1)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_S], e, 0, &patch1)); } + else if (!typecmp( e->type, type_float) && flag_iffloat) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF_F], e, 0, &patch1)); else QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IF], e, 0, &patch1)); } @@ -5734,8 +5862,10 @@ void QCC_PR_ParseStatement (void) if (!typecmp( e->type, type_string) && flag_ifstring) { QCC_PR_ParseWarning(WARN_IFSTRING_USED, "if (string) can result in bizzare behaviour"); - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOTS], e, 0, &patch1)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch1)); } + else if (!typecmp( e->type, type_float) && flag_iffloat) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch1)); else QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch1)); } @@ -5999,7 +6129,9 @@ void QCC_PR_ParseStatement (void) else { if (e->type->type == ev_string) - QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOTS], e, 0, &patch3)); + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_S], e, 0, &patch3)); + else if (e->type->type == ev_float) + QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT_F], e, 0, &patch3)); else QCC_FreeTemp(QCC_PR_Statement (&pr_opcodes[OP_IFNOT], e, 0, &patch3)); } @@ -7081,11 +7213,16 @@ QCC_function_t *QCC_PR_ParseImmediateStatements (QCC_type_t *type) // hexenC has void name() : 2; if (QCC_PR_CheckToken ("#") || QCC_PR_CheckToken (":")) { - if (pr_token_type != tt_immediate - || pr_immediate_type != type_float - || pr_immediate._float != (int)pr_immediate._float) + int binum = 0; + if (pr_token_type == tt_immediate + && pr_immediate_type == type_float + && pr_immediate._float == (int)pr_immediate._float) + binum = (int)pr_immediate._float; + else if (pr_token_type == tt_immediate && pr_immediate_type == type_integer) + binum = pr_immediate._int; + else QCC_PR_ParseError (ERR_BADBUILTINIMMEDIATE, "Bad builtin immediate"); - f->builtin = (int)pr_immediate._float; + f->builtin = binum; QCC_PR_Lex (); locals_start = locals_end = OFS_PARM0; //hmm... @@ -8912,7 +9049,7 @@ void QCC_PR_ParseDefs (char *classname) if (d->constant) { for (i = 0; i < d->type->size; i++) - G_INT(def->ofs) = G_INT(d->ofs); + G_INT(def->ofs+i) = G_INT(d->ofs+i); def->constant = !isvar; def->initialized = 1; continue; @@ -9399,14 +9536,14 @@ void QCC_PR_ParseDefs (char *classname) else def->constant = true; def->initialized = 1; + + if (pr_immediate_type->type != ev_vector) + QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); (((float *)qcc_pr_globals)[def->ofs+0]) = pr_immediate.vector[0]; (((float *)qcc_pr_globals)[def->ofs+1]) = pr_immediate.vector[1]; (((float *)qcc_pr_globals)[def->ofs+2]) = pr_immediate.vector[2]; QCC_PR_Lex (); - if (pr_immediate_type->type != ev_vector) - QCC_PR_ParseError (ERR_BADIMMEDIATETYPE, "wrong immediate type for %s", name); - continue; } else diff --git a/engine/qclib/qcc_pr_lex.c b/engine/qclib/qcc_pr_lex.c index 84c0e4e8..ecca9ce5 100644 --- a/engine/qclib/qcc_pr_lex.c +++ b/engine/qclib/qcc_pr_lex.c @@ -1397,8 +1397,16 @@ void QCC_PR_LexNumber (void) pr_file_p++; } - pr_immediate_type = type_float; - pr_immediate._float = (float)(num*sign); + if (flag_assume_integer) + { + pr_immediate_type = type_integer; + pr_immediate._int = num*sign; + } + else + { + pr_immediate_type = type_float; + pr_immediate._float = (float)(num*sign); + } } @@ -2502,11 +2510,11 @@ void QCC_PR_Lex (void) if ( (c == '.'&&pr_file_p[1] >='0' && pr_file_p[1] <= '9') || (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') ) { pr_token_type = tt_immediate; - pr_immediate_type = type_float; - pr_immediate._float = QCC_PR_LexFloat (); +// pr_immediate_type = type_float; +// pr_immediate._float = QCC_PR_LexFloat (); -// pr_token_type = tt_immediate; -// QCC_PR_LexNumber (); + pr_token_type = tt_immediate; + QCC_PR_LexNumber (); return; } diff --git a/engine/qclib/qccmain.c b/engine/qclib/qccmain.c index 4f63cd7c..a293f2f4 100644 --- a/engine/qclib/qccmain.c +++ b/engine/qclib/qccmain.c @@ -224,12 +224,14 @@ compiler_flag_t compiler_flag[] = { {&autoprototype, 0, "autoproto", "Automatic Prototyping","Causes compilation to take two passes instead of one. The first pass, only the definitions are read. The second pass actually compiles your code. This means you never have to remember to prototype functions again."}, //so you no longer need to prototype functions and things in advance. {&writeasm, 0, "wasm", "Dump Assembler", "Writes out a qc.asm which contains all your functions but in assembler. This is a great way to look for bugs in fteqcc, but can also be used to see exactly what your functions turn into, and thus how to optimise statements better."}, //spit out a qc.asm file, containing an assembler dump of the ENTIRE progs. (Doesn't include initialisation of constants) {&flag_ifstring, FLAG_MIDCOMPILE,"ifstring", "if(string) fix", "Causes if(string) to behave identically to if(string!="") This is most useful with addons of course, but also has adverse effects with FRIK_FILE's fgets, where it becomes impossible to determin the end of the file. In such a case, you can still use asm {IF string 2;RETURN} to detect eof and leave the function."}, //correction for if(string) no-ifstring to get the standard behaviour. + {&flag_iffloat, FLAG_MIDCOMPILE,"iffloat", "if(-0.0) fix", "Fixes certain floating point logic."}, {&flag_acc, 0, "acc", "Reacc support", "Reacc is a pascall like compiler. It was released before the Quake source was released. This flag has a few effects. It sorts all qc files in the current directory into alphabetical order to compile them. It also allows Reacc global/field distinctions, as well as allows ¦ as EOF. Whilst case insensativity and lax type checking are supported by reacc, they are seperate compiler flags in fteqcc."}, //reacc like behaviour of src files. {&flag_caseinsensative, 0, "caseinsens", "Case insensativity", "Causes fteqcc to become case insensative whilst compiling names. It's generally not advised to use this as it compiles a little more slowly and provides little benefit. However, it is required for full reacc support."}, //symbols will be matched to an insensative case if the specified case doesn't exist. This should b usable for any mod {&flag_laxcasts, FLAG_MIDCOMPILE,"lax", "Lax type checks", "Disables many errors (generating warnings instead) when function calls or operations refer to two normally incompatible types. This is required for reacc support, and can also allow certain (evil) mods to compile that were originally written for frikqcc."}, //Allow lax casting. This'll produce loadsa warnings of course. But allows compilation of certain dodgy code. - {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, - {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions. This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, - {&flag_fasttrackarrays, FLAG_MIDCOMPILE|FLAG_ASDEFAULT,"fastarrays", "fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, //correction for if(string) no-ifstring to get the standard behaviour. + {&flag_hashonly, FLAG_MIDCOMPILE,"hashonly", "Hash-only constants", "Allows use of only #constant for precompiler constants, allows certain preqcc using mods to compile"}, + {&opt_logicops, FLAG_MIDCOMPILE,"lo", "Logic ops", "This changes the behaviour of your code. It generates additional if operations to early-out in if statements. With this flag, the line if (0 && somefunction()) will never call the function. It can thus be considered an optimisation. However, due to the change of behaviour, it is not considered so by fteqcc. Note that due to inprecisions with floats, this flag can cause runaway loop errors within the player walk and run functions (without iffloat also enabled). This code is advised:\nplayer_stand1:\n if (self.velocity_x || self.velocity_y)\nplayer_run\n if (!(self.velocity_x || self.velocity_y))"}, + {&flag_fasttrackarrays, FLAG_MIDCOMPILE|FLAG_ASDEFAULT,"fastarrays","fast arrays where possible", "Generates extra instructions inside array handling functions to detect engine and use extension opcodes only in supporting engines.\nAdds a global which is set by the engine if the engine supports the extra opcodes. Note that this applies to all arrays or none."}, + {&flag_assume_integer, FLAG_MIDCOMPILE,"assumeint", "Assume Integers", "Numerical constants are assumed to be integers, instead of floats."}, {NULL} }; @@ -3198,7 +3200,7 @@ void QCC_FinishCompile(void) } }*/ - if (pr_werror && pr_werror) + if (pr_werror && pr_warning_count != 0) QCC_Error (ERR_PARSEERRORS, "compilation errors"); // write progdefs.h diff --git a/engine/qclib/test.c b/engine/qclib/test.c index eec05fbb..8875d2eb 100644 --- a/engine/qclib/test.c +++ b/engine/qclib/test.c @@ -12,12 +12,13 @@ #include #include #include +#include //builtins and builtin management. -void PF_print (progfuncs_t *prinst, struct globalvars_s *gvars) +void PF_prints (progfuncs_t *prinst, struct globalvars_s *gvars) { char *s; s = prinst->VarString(prinst, 0); @@ -25,9 +26,27 @@ void PF_print (progfuncs_t *prinst, struct globalvars_s *gvars) printf("%s", s); } +void PF_printv (progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + printf("%f %f %f\n", G_FLOAT(OFS_PARM0+0), G_FLOAT(OFS_PARM0+1), G_FLOAT(OFS_PARM0+2)); +} + +void PF_printf (progfuncs_t *prinst, struct globalvars_s *pr_globals) +{ + printf("%f\n", G_FLOAT(OFS_PARM0)); +} + + +void PF_bad (progfuncs_t *prinst, struct globalvars_s *gvars) +{ + printf("bad builtin\n"); +} + builtin_t builtins[] = { - PF_print, - PF_print + PF_bad, + PF_prints, + PF_printv, + PF_printf }; @@ -36,7 +55,10 @@ builtin_t builtins[] = { //Called when the qc library has some sort of serious error. void Sys_Abort(char *s, ...) { //quake handles this with a longjmp. - printf("%s", s); + va_list ap; + va_start(ap, s); + vprintf(s, ap); + va_end(ap); exit(1); } //Called when the library has something to say. @@ -109,10 +131,13 @@ void runtest(char *progsname) ext.Abort = Sys_Abort; ext.printf = printf; + ext.numglobalbuiltins = sizeof(builtins)/sizeof(builtins[0]); + ext.globalbuiltins = builtins; + pf = InitProgs(&ext); pf->Configure(pf, 1024*1024, 1); //memory quantity of 1mb. Maximum progs loadable into the instance of 1 //If you support multiple progs types, you should tell the VM the offsets here, via RegisterFieldVar - pn = pf->LoadProgs(pf, progsname, 0, builtins, sizeof(builtins)/sizeof(builtins[0])); //load the progs, don't care about the crc, and use those builtins. + pn = pf->LoadProgs(pf, progsname, 0, NULL, 0); //load the progs, don't care about the crc, and use those builtins. if (pn < 0) printf("test: Failed to load progs \"%s\"\n", progsname); else @@ -185,11 +210,11 @@ int main(int argc, char **argv) { if (argc < 2) { - printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat --srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]); + printf("Invalid arguments!\nPlease run as, for example:\n%s testprogs.dat -srcfile progs.src\nThe first argument is the name of the progs.dat to run, the remaining arguments are the qcc args to use", argv[0]); return 0; } - compile(argc-2, argv+2); + compile(argc-1, argv+1); runtest(argv[1]); return 0;