Title    : qccx v1.0 optimizing QuakeC compiler
Filename : qccx100.zip
Version  : 1.0
Date     : March 3, 2000
Author   : J.P. Grossman (a.k.a. Mephistopheles)
Email    : jpg@ai.mit.edu
URL      : http://elohim.ai.mit.edu/qccx


    /------------------------------------\
1.0 Optimizations and Command Line Options
    \------------------------------------/

Used with no command line options, qccx will produce the same progs.dat
produced by fastqcc, only much faster.  The easiest way to turn on
optimizations is to include /O2 in the command line which will enable all
optimizations.  Alternately, you can enable optimizations individually
using the /Ot, /Oi, /Op, /Oc, /Od and /Os options (described in the following
sections).  Compiling with /O2 is equivalent to compiling with all six command
line options.  Note that in each case O is the letter O, not zero.

1.1 /Ot - eliminate temporary variables

Peephole optimization: changes

OP    A, B, temp
STORE temp, C

to

OP    A, B, C

1.2 /Oi - shorten ifs

Peephole optimization: changes

NOT   A, temp
IFNOT temp

to

IF    A

1.3 /Op - non-vector parms

Save a few cycles by copying non-vector function parameters with a STORE
rather than a STOREV.

1.4 /Oc - eliminate constant defs/names

The names and associated defs of initialized (constant) variables are not
stored in the progs.dat.  This is a space optimization and does not affect 
speed.

1.5 /Od - eliminate duplicate defs

If two initialized variables have the same value, only one def is used.
Another space optimization.

1.6 /Os - hash lookup in CopyString

Using this option avoids duplicate strings in the string table; another space
optimization.

     /------\
2.0  Integers
     \------/

Standard QuakeC stores ALL numbers in floating point format, so for example
the "integer" 1 is stored internally as 3f800000.  qccx lets you create
bona-fide integers by prefixing them with %, e.g. %5 or %-3.  There is 
no new type for integers; they are stored in floats.  On their own they are
not useful; they are used for arrays and pointer arithmetic.  The following
example illustrates the creation of integer constants:

local float a, b, c, d;

a = 5;    // stored as 40a00000
b = %5;   // stored as 00000005
c = -3;   // stored as c0400000
d = %-3;  // stored as fffffffd

Integer literals may also be entered in hexadecimal C-style, e.g.

a = 0x00000001;  // same as a = %1
a = 0xfffffffd;  // same as a = %-3
a = 0x1df001ed;

Be careful when adding or subtrating integers; the following rule applies:
If both operands *and* the result are POSITIVE INTEGERS LESS THAN 2^24, then
you can use the standard + and - operators as you would with floating point
numbers.  However, if either operand or the result is NEGATIVE OR GREATER
THAN 2^24-1, you *must* use the AddInt / SubInt functions supplied in 
internal.qc, which are less efficient but guaranteed to work.  If you're 
ever unsure, just use AddInt/SubInt.  The following example illustrates this:

local float a, b, c;

a = %24;
b = %192;
c = a + b;         // correct; a, b, c are positive and less than 2^24
c = b - a;         // correct; a, b, c are positive and less than 2^24
c = a - b;         // *** WRONG!!! ***  The result is negative
c = SubInt(a, b);  // correct
a = b + c;         // *** WRONG!!! ***  c is negative
a = AddInt(b, c);  // correct

To convert between POSITIVE floating point and integer representations, simply 
multiply or divide by %1.  This *only* works for positive numbers; you must
use NegInt to deal with negative numbers.  Example:

local float a;

a = 5;      // stored as 40a00000 (floating point 5)
a = a * %1; // changes a to 00000005 (integer 5)
a = a / %1; // changes a back to 40a0000 (floating point 5)
a = %-3;
b = a / %1; // *** WRONG!!! ***  This won't work because a is negative
b = -(NegInt(a) / %1); // correct

Finally, when comparing two integers, always prefix them with * (which
casts them to entities.. see section 3).  Example:

if (a == b)   // This is wrong if a and b contain integer values
if (*a == *b) // correct

     /-----------------\
3.0  Pointers and Arrays
     \-----------------/

There are several different types of pointers; all of them are (as with
integers) stored in float variables.  When you are programming it is 
important to remember which pointers are of which type and perform the
appropriate conversions where necessary.

3.1 Arrays

The best way to explain arrays is to explain how QuakeC works with entities
and fields.  Consider the expression self.health.  "self" is an entity, which
actually holds a byte offset from the internal array of entities.  "health"
is really just an integer that gives the dword (not byte) offset of the field 
in the entity (specifically, health = 48).  Loosely speaking, the quake engine
sees self.health and translates it to "the value stored at 
entities + self + 4*health".

qccx allows the use of integers rather than fields to specify an offset
within an entity; standard [] notation is used.  So, for example, self[%48]
is an exact synonym for self.health.  Warning: a common mistake (for me, 
at least) is to type self[48] instead of self[%48].  This is wrong!  Recall
that 48 isn't really an integer.. it is a floating point number.  The quake
engine doesn't perform any sort of bounds checking, so the expression 
self[48] in a quakeC program will cause quake to crash.

So now you know how to treat an entity as an array.  This isn't very useful
though; if we make the mistake of simply spawning a new entity and using it as
an array, we will most likely quickly overwrite the .movetype field (at 
offset 8) which will cause quake to puke.  We could solve this problem by
"allocating" the array at a large enough offset in the entity to avoid
overwriting anything that the quake engine cares about (150 is large enough)
and then always index array elements as e[i + %150].  What we would really
like to be able to do, though, is to somehow offset e itself so that it 
points to the middle of the entity, or maybe even to a completely different
part of memory.  For this we use..

3.1 Entity (QuakeC) pointers

qccx provides the & operator to get the address of an entity (referred to as
a quakeC pointer) and the * operator for dereferencing an entity pointer 
(under the hood, & is really just a cast from entity to float and * is a cast
from float to entity.. but it's probably best to think of it as address of and
dereference as in C).  Integer arithmetic can be used to modify entity 
pointers.  For example, the following code spawns a new enitity and then sets 
up an array at an offset of 150 into the entity (that's an offset of 150 
dwords, which is the same as 600 bytes):

local entity e, array;

e = spawn();
array = *(&e + %600);

It is a fact that all pointers to spawned entities are positive integers less
than 2^24, which is why we don't have to use AddInt in the above example.  
Another useful fact is that &world = 0, so we could create an array at dword
offset 150 within world without spawning a new entity simply by setting
array = *%600.

3.2 String pointers

qccx provides the & operator to get the address of a string and the @ operator
for dereferencing a string pointer.  As with entities, these are really just
casts between the float and string types, but it is best to think of them
as address of and dereference operators.  The following example illustrates
the use of string pointers:

local string s = "This is a string pointer";
dprint(@(&s + %5), "\n");

Output:  "is a string pointer"

Another useful fact is that for any string s defined in QuakeC, &s is a 
positive integer less than 2^24, which is why we again didn't have to use
AddInt.  However, for any other string this is in general *not* true;
examples include world.mapname, self.netname, and the return values of
ftos and vtos.  Therefore you *must* use AddInt to manipulate pointers
to these strings.

Unlike C, different types of pointers to the same piece of data are NOT the
same.  So, for example, you can't use the * operator on a string pointer and 
get reasonable results.  To convert a string pointer to a quakeC pointer that
will allow you to inspect and modify the contents of the string, the integer 
constant PSTRING_TO_PQUAKEC (defined in internal.qc)  must be added to the 
pointer.  Simlarly, to convert from a quakeC pointer to a string pointer the
integer PQUAKEC_TO_PSTRING must be added.  The following code illustrates
these concepts:

local entity e;
local string s = "Hi tXXXXhere";
local float psource, pdest;

e = *%600;
psource = AddInt(&s, PSTRING_TO_PQUAKEC);
e[0] = (*psource)[0];
e[%1] = (*psource)[%2];
pdest = AddInt(&e, PQUAKEC_TO_PSTRING);
dprint(@pdest, "\n");

Output:  "Hi there"

3.3 C pointers

If you start hacking around with quake internals, you will come across
pointers stored in the data structures.  You can read them, but what do you
do with them?  The answer is that you convert them from C pointers to 
quakeC pointers by adding the constant PC_TO_PQUAKEC (defined in internal.qc).
See DumpCL in client.qc for an example of how this is used.  internal.qc
also defines the constants PC_TO_PSTRING, PQUAKEC_TO_PC and PSTRING_TO_PC, 
all of which do the obvious thing.

3.4 Raw pointers

When the & operator is applied to an integer, it creates a raw pointer.
This is a byte index directly into the internal entities array.  It can
only be used to store data; it cannot be used to read data.  Don't use it.
I only mention it for completeness and because it is used for efficiency 
in the integer routines in internal.qc.  In fact, forget you even read this.

3.5 Fun with &

You can also use the & operator on fields and functions.  In each case it is
really just a cast to a float.  Using it on a field gives you the index of
the field, so for example self.health and self[&health] are exactly 
synonymous.  Using it on a function gives you the offset of the function
from the start of the functions in progs.dat (see the fnstring function in 
internal.qc).

     /-----\
4.0  Strings
     \-----/

4.1 [] operator

The [] operator is provided as a convenience to index into strings.  s[i] is
exactly the same thing as @(&s + i).  Thus, the usual rules apply: you can
only use the [] operator if &s is positive (i.e. a string defined in quakeC).
Note that here i is a byte index, not a dword index.

4.2 Concatenated literals

String literals can be concatenated by placing them consecutively in the code,
for example "hi " "there" is the same as "hi there".  To include the null 
terminations, separate the strings by :, e.g. "hi ":"there".  See the hex
string and its use in internal.qc for an example.

4.3 Escape sequences

The following escape sequences have been added:

\\   - backslash
\"   - double quote
\b   - toggle bronze characters
\[   - gold [ character
\]   - gold ] character
\.   - centerdot
\<   - character 29
\-   - character 30
\>   - character 31
\(   - character 128
\=   - character 129
\)   - character 130
\{n} - character n (0 <= n <= 255)

See ClientKill in client.qc for an example of how to use these.

     /-----------\
5.0  Other Changes
     \-----------/

5.1 String Constants

qccx allows you to create string constants by initializing string variables
in the same way that other variables can be initialized.

5.2 for loops

C style for loops are supported.  The syntax is exactly the same as in C.

5.3 Properly typed vararg functions

When a vararg function is defined, qccx will type check the typed parameters
each time the function is used.  For example, the declaration for sprint is

void (entity client, string s, ...) sprint = #24;

so when qccx parses the following function call:

sprint(self, s1, s2, s3, s4);

it will type check self and s1, but not s2, s3 or s4.

5.4 Parameter omission optimization

QuakeC uses 8 global variables for parameter passing.  When a function is
called, the parameters are copied to these global variables.  If two 
functions in a row are called where (1) one of the parameters is the
same, and (2) the first function doesn't overwrite these global variables,
then it is not actually necessary to copy the parameter a second time.
The # symbol may be used to denote an unchanged parameter that does
not need to be copied.  See the function "twice" in world.qc for an example.
