Friday, December 14. 2007

TinyCC -- a fast C compiler

I've just found a nice, small C compiler: tinycc. The compiler includes the actual compiler and the linker, so you don't need other external tools. The executable is only 122KB in size on my system (Fedora Core 6). I like tcc because it allows you to create C-scripts.

#!/usr/local/bin/tcc -run

#include <stdio.h>
#include <stdlib.h>

double fac(double n) {
  if (n <= 0.0) return 1;
  return n * fac(n - 1);
}

int main(int argc, char* argv[]) {
  if (argc != 2) {
    printf("Usage: %s <number>\n", argv[0]);
    return -1;
  }
  double n = atof(argv[1]);
  printf("fac(%.0lf) = %.0lf\n", n, fac(n));
  return 0;
}

If you make the file executable, tcc will compile and run the program right away as you've always done it with perl or other scripting languages.

The compiling is pretty fast, too. TCCBOOT claims it can compile the Linux kernel in 15 seconds. I have not verified this claim, but I see that compiling the tcc sources with gcc takes about 8.6 seconds on my machine, while compiling the tcc source with tcc takes 0.2 seconds.

Of course, this speedup in compilation and linking time comes with a price: the code is not as optimized as with gcc. Calculating the factorial of 47 one million times takes the following times:

gcc default:  0.992 sec
gcc -Os:      0.003 sec
gcc -O1:      0.003 sec
gcc -O2:      0.002 sec
gcc -O3:      0.350 sec

tcc compiled: 1.076 sec
tcc -run:     0.443 sec

So, the run time of a program compiled with tcc is about the same as when compiled with gcc with no optimizations turned on. However, the run time decreases dramatically when gcc optimizes. Note that running the small test program directly with tcc -run fac.c 47 is twice as fast as running the compiled program, although tcc -run first compiles the code!

Besides compiling fast, tcc also has a nice debugging feature. Where with gcc compiled programs you just get a "Segmentation fault" message, tcc -g gives you a backtrace pointing you to the location of the segfault:

$ tcc -g -run fac.c 1
Runtime error: dereferencing invalid pointer
at 0x0946b437: fac() (fac.c:9)
by 0x0946b45d: fac() (fac.c:10)
by 0x0946b4c5: main() (fac.c:19)
by 0x0805bb54: ???
by 0x0805ea9e: ???
by 0x007f2f2c: ???

I guess, tcc's fast compilation and helpful debug messages will increase my C coding turnaround. You can download the source of tcc at http://fabrice.bellard.free.fr/tcc/. There are source and binary packages. On my Fedora Core 6, tcc did not compile (due to my GNU style /usr/lib/libc.so linker script) and the -run feature segfaulted. The current CVS snapshot does not have these issues. You can use my RPM package of the 2007-14-12 CVS snapshot of tcc.