It was less than a year ago that I wrote about linker hacking the
runtime out of D code so that it could work as “better C” code, but things have already changed a lot since then. A
few days ago Walter Bright announced a new, improved
-betterC
switch, which can now do a lot of the stuff that
needed ugly hacking before.
What is -betterC
, and Why do I Need it?
The short answer is that most D programmers don’t need it. The longer answer is that it does two things: first, it restricts the language to a lower-level subset (that’s still higher-level than C), and, second, it changes the implementation of compiled code a little so that it only depends on the C runtime, and not the D runtime.
If you just want control over things like GC and runtime features for performance reasons, you can already get it
without -betterC
. You can read more about that in this GC series on the official blog, and in my previous post about the D runtime itself.
What -betterC
does provide is an intermediate
language that integrates very well with both C code and D code. Walter envisions this as a way for D to penetrate more
into parts of the software world that are still dominated by C. For example, practically all languages today still run
on top of a layer of operating system libraries that are written in C (and C++ in the Windows world). D’s runtime
itself depends on this layer, so D can’t ever replace C unless runtimeless programming is possible.
The -betterC
switch is a little controversial, and I agree
that things could be better in the future. But -betterC
is
here today, and ultimately we’re only going to figure out how to use D as a better C by trying it out. That’s why I
originally published that post about runtimeless D, even though it was a horrible hack.
What’s New?
There are two main ways betterC programming has improved since I last wrote about it. One pain point was the
over-dependence on runtime reflection in the language implementation, even for things like integer array comparisons
that could be implemented with just memcmp()
. A lot of work
has been done since then to replace reflection with templates, which is good news even for programmers who aren’t doing
low-level stuff. Lucia Cojocaru presented some of this work at
DConf 2017.
The other area of improvement is in the -betterC
switch
itself. Back then there were only two places in the DMD compiler where -betterC
had any effect at all, so most runtime dependencies were left
in. Simply defining a struct, for example, would still cause the D compiler to insert TypeInfo
instances for runtime type information, which depend on base
class implementations that are defined in the D runtime library. assert
statements would still be implemented using a D runtime
implementation, not the C runtime implementation. These are the two most obvious problems that have been fixed.
-betterC
Take Two
In that old post, I took some D code, compiled it, hacked out the runtime, and then linked it directly to some C code without the D runtime. Let’s see how things work now. Here’s the D code again:
And here’s the C code:
Update: this didn’t quite work on an older version of DMD, but it does since 2.079. Leaving this here for the sake of history.
Here’s what happens now (on a GNU/Linux system):
Damn. So close. The D compiler has left in some exception handling data structures, even though -betterC
isn’t supposed to support exceptions. You’ll see I’m using the
new DMD beta, and there’s already an open bug report and pull
request for this kind of problem, so I expect it’ll be fixed soon. I’ll update this post when it
is.(Done.)
Here’s a quick workaround for now (read the original linker hacking article for an explanation):
It might not look like much, but it’s a huge improvement. Thanks to all the developers who helped make it happen.