28 comments

[ 2.8 ms ] story [ 51.2 ms ] thread
What a blast from the past TCC!

Sad but not surprised to see it's no longer maintained (8 years ago!).

Even in the era of terabyte NVMe drives my eyes water when I install MSVC (and that's usually just for the linker!)

This was the compiler I was required to use for my courses in university. GCC was forbidden. The professor just really liked tcc for some reason.
TCC is fantastic! Very hackable, easy to compile to WASM for some interesting in-browser compilation
Man I can't wait for tcc to be reposted for the 4th time this week with the license scrubbed and the comment of "The Latest AI just zero-shotted an entire C compiler in 5 minutes!"
Does anyone use libtcc for a scripting language backend? Smaller and faster than llvm. You'd have to transpile to a C ast I imagine.
TCC is fantastic! I use it a lot to do fast native-code generation for language projects, and it works really really well.
I mixed it up with LCC which was used in Quake 3. Still this is pretty cool.
I recall, there where similar items back in late 70s and early 80s .

Tiny C, Small C are names I seem to recall, buts its very fuzzy - Not sure if they were compilers, may have been interpreters....

You probably remember cint
The code of TCC (0.9.26) is kind hard to compile, I have discovered in the past year, while developing a minimal C compiler to compile the TCC sources [1]. For that reason, I have concluded that TCC is its own test set. It uses the constant 0x80000000, which is an edge case for if you want to print it as a signed integer only using 32-bit operators. There is a switch statement with an post-increment operator in the switch expression. There are also switch statements with fall throughs and with goto statements in the cases. It uses the ## operator where the result is the name of a macro. Just to name a few.

[1] https://github.com/FransFaase/MES-replacement

To be honest, these all seem like pretty basic features.

Goto is easier to implement than an if statement. Postincrement behaves no differently in a switch statement than elsewhere.

Anyone know a good resource for getting started writing a compiler? I'm not trying to write a new LLVM, but being a "software engineer" writing web-based APIs for a living is leaving me wanting more.
Currently striving towards my own TypeScript to native x86_64 physical compiler quine bootstrapped off of TCC and QuickJS. Bytecode and AST are there!
What advantage does this have over SDCC?
TCC supports 32 (and I think 64?) bit chips, SDCC only targets 8 and 16, so their use cases don't overlap at all as far as I can tell from their homepages...
One of the coolest tricks is using tcc to compile "on demand." This allows you to use a compiled language like Nim for scripting, with almost no noticeable performance difference compared to interpreted languages.

  #!/usr/bin/env -S nim r --cc:tcc -d:useMalloc --verbosity:0 --hints:off --tlsEmulation:on --passL:-lm
  echo "Hello from Nim via TCC!"
Here's a comparison (bash script at [1]) of a minimal binary compiled this way with different interpreters. First line is the noise. Measured by tim[2] written by @cb321.

  1.151 +- 0.028 ms     (AlreadySubtracted)Overhead
  1.219 +- 0.037 ms     bash -c exit
  2.498 +- 0.040 ms     fish --no-config --private -c exit
  1.682 +- 0.058 ms     perl   -e 'exit 0'
  1.621 +- 0.043 ms     gawk   'BEGIN{exit 0}'
  15.8 +- 2.2 ms     python3 -c 'exit(0)'
  20.0 +- 5.7 ms     node   -e 'process.exit(0)'
  -2.384 +- 0.041 ms tcc -run x.c
  153.2 +- 4.6 ms     nim r --cc:tcc  x.nim
  164.5 +- 1.2 ms     nim r --cc:tcc -d:release x.nim
Measured on a laptop without any care to clean the environment, except turning the performance governor. Even with `-d:release` compiling nim code is comparable.

The fact that tcc compilation cycle measures negative here is a nice punchline.

[1]: https://gist.github.com/ZoomRmc/58743a34d3bb222aa5ec02a5e2b6...

[2]: https://github.com/c-blake/bu/blob/main/doc/tim.md

It's worth pointing out that Nim is going to cache all of the compilation up to the linking step. If you want to include the full compilation time, you'd need to add --forceBuild to the Nim compiler.

(Since a lot of the stuff you'd use this for doesn't change often, I don't think this invalidates the "point", since it makes "nim r" run very quickly, but still)

There's also the Nim interpreter built into the compiler, "NimScript", which can be invoked like:

  #!/usr/bin/env -S nim e --hints:off
  echo "Hello from Nim!"
The cool thing is that, without --forceBuild, Nim + TCC (as a linker) has a faster startup time than NimScript. But if you include compile time, NimScript wins.
No, the coolest thing about tcc is that it is developed successfully wiki-style. Everybody can commit to the main branch (called mob). And it only gets better. No cooperation would allow that.
TCC is my go-to for keeping builds lean. on windows specifically, you get a functional C compiler in a few hundred KB, whereas the standard alternatives require gigabytes of disk space (that I don't have to spare) and complex environment setups
What's the quality of the generated code like? Does it use explicit stack frames and all local variables live there? Does it move loop-invariant operations out of a loop? Does it store variables in registers?