six thoughts on generating c

33 points by fanf


osa1

I also finished my language's reference implementation's C backend recently. My #1 tip if you're considering generating C would be: get familiar with statement expressions and their limitations.

(Yes they're a gcc extension, but clang also supports them.)

They're really helpful when compiling conditional or pattern matching expressions to C. They have a few limitations explained in the link, but the big one for me (which I think isn't mentioned in the link actually) was that you can't generate a return in a statement expression and expect the expression's type to just match the expected one from the context. So let's say I'm generating a function call in C: f(arg1, arg2, arg3), and arg2 is an expression in my language that can't be an expression in C, so I use a statement expression like

f(
    arg1,      // compiled to a C expr
    ({ ... }), // statement expression here
    arg3,      // compiled to a C expr
)

In that ... part I can have a return, but I still need an expression as the last statement. This isn't valid:

f(
    arg1,
    ({ statement1;
       return 123; }),
    arg3,
)

Because that last thing needs to be an expression. So I have to generate a dummy value after the return, with the right type:

f(
    arg1,
    ({ statement1;
       return 123;
       (MyStruct) {0}; }), // the actual dummy value depends on the type expected
    arg3,
)

Initially I was YOLOing my way without having the expected type in my expression compiler. When I discovered this I had to manually plumb expression types all the way from the type checker through monomorphic AST and lowered AST, and then to the expression compiler...

manuel

always-inline functions remove any possible performance penalty for data abstractions

I find this to be slightly hyperbolic. It's only true if code size is not part of what you're measuring as "performance" (e.g. on the web/Wasm you want to produce as tiny executables as possible). It also ignores downstream effects e.g. on the instruction cache.

icefox

Counterpoint, I've been generating Rust code as a placeholder for my language backend for years. It's certainly not flawless, but it's stood up to the test of time wayyyyy better than I expected.