SDCC is a stanard C compiler (C89 to C11) for 8-bit architectures, mostly for freestanding implementation (hosted implementation requires more work). Includes supporting binutils and runs on many hosts. It has some unusual optimisations that make sense for 8-bit processors, particularly in the register allocator.
C standards give some leeway to the compiler that allow it to optimize better, and this is used by SDCC. C89 is complete except for doubles and struct assignment (you have to memcpy them explicitly). From C99, declarations in the code and VLAs are missing, as well as compound literals. But the stdint and bool types are supported. From C11, generics and unicode support are missing, but noreturn and static assert are supported and used for optimisation.
Many target architectures are supported, but usually not very complete because it is very difficult to generate code for them, e.g. because it is difficult to access the stack. On some architectures, it is possible to use complex instructions to do many operations with a single instruction, e.g. memcpy. Commercial compilers still perform better (in code size), but only by a margin of about 30%.
Optimisations are done first target-independent on the iCode, then target-specific, and then register allocation. After assembly generation, an additional peephole optimisation step is performed. The register allocation step is very important because spilling to memory means additional data memory and additional code to do the spill – even when doing direct-memory operations, the instructions that use registers are usually shorter than the instructions that use memory.
For these 8-bit targets, register allocation is complicated because there are different register types, and not all operations access all registers equally. Sometimes it is cheaper to recalculate than to store in memory. Also often a 16-bit register is aliased with two 8-bit registers.
The SDCC register allocator is based on graph-structure theory. It can do theoretically optimal register allocation in polynomial time, but that means it is slow for targets with many registers. Therefore, there is a speed/code quality trade-off option. Bytewise register allocation decides for every byte of a variable whether it goes to memory or register.
The project does regression testing of daily snapshots, running 7000 test from GCC and from fixed bugs. This is done for various targets and various hosts.