home *** CD-ROM | disk | FTP | other *** search
- Compiler optimizations
- ======================
-
- The compiler applies a few easy to implement optimizations to generate
- small & fast code. The first two (as described below) give the Drystone
- benchmark a considerable speed boost (almost twice as fast).
- These optimizations increase compilation time, but because the compiler
- compiles itself, the slowdown is more than made up for by the higher quality
- code.
- They are also performed by the DICE compiler and assembler.
- The commercial M2Amiga compiler (I paid $200 back in 1991) used to
- bootstrap m2c generates relatively larger and slower code becuase it
- does not attempt to do any of the following.
-
- Register Allocation
- ===================
-
- Simple local variables and parameters are allocated to registers based on
- wieghted usage.
- Parameters are copied off the stack and into registers at procedure entry.
- Locals used inside nested loops are given highest priority.
- Typically there are 6 data registers (d2-d7) and 4 address registers
- (a2,a3,a5,a6) available.
- Procedures that contain complex expressions may have less registers
- available.
- Low level procedures (that call no others) can also use d0,d1,a0,a1 to hold
- locals.
- Locals that are passed as VAR parameters to other procedures, arguments of
- SYSTEM.ADR or locals referenced from within nested procedures
- (so called non-local,non-global variables) cannot be allocated to registers.
-
- LINK/UNLINK removal
- ===================
-
- If the compiler manages to allocate all parameters and variables for
- a particular procedure to registers.Then it can remove the LINK & UNLINK
- instruction pair which normally constructs/destructs a stack frame for
- variables in that procedure.
- If this can be done the procedure call overhead is greatly reduced. This is
- specially important in small procedures eg. when using opaque types we
- are sometimes forced to use a function just to read the contents of a field.
-
- CASE statement
- ==============
-
- Generating code for the case statements is quite tricky. The compiler must
- adequately cope with a variety of styles:
-
- CASE exp OF
- |-42 : ..
- |10000: .. (* This is bad style but is still legal. *)
- END (* Some (inadequate) compilers will complain if you try this *)
-
-
- or
- ==
-
- CASE exp OF
- |0: ..
- |1: ..
- |2: ..
- |3: .. (* Case labels are contiguous *)
- ... (* This is really what the case statement is for *)
- |100: ..
- END ;
-
- or a combination of the 2 styles.
-
- The algorithm for the case statement is:
-
- If there are only a few case labels (less than 8 say) then perform a linear
- search.
- If the number of case labels is a high proportion (50 percent say) of
- MAX(label)-MIN(label) then perform a table lookup to see where to branch to.
- Otherwise subdivide the labels (into 2 havles) and recursively apply the
- algorithm.
-
- The compiler generates code to implement this algorithm.
-
- Branch Optimization
- ===================
-
- The M68000 provides a 16bit as well as 32bit branch instruction.
- When the compiler generates a forward branch its impossible for it to know
- which to use.To be safe it generates space for a 32bit instruction, if
- subsequently it turns out that only a 16bit instruction was needed, the code
- will contain a 2 bytes hole. The compiler keeps track of where these holes
- occur and after code generation compacts all the code.
- Branch optimization typically reduces code size by about 5 percent.
-
- Future optimizations
- ====================
-
- The compiler does not implement registerized parameter passing, this may
- be added. It doesn't do any 'global' optimizations either because I dont
- know how :-) This is only my second compiler (the first was for a toy
- language at college).
-
-
-