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:

module count;


import core.atomic : atomicOp, atomicLoad;

        int count()
                scope(exit) counter.addOne();
                return counter.getValue();


shared struct AtomicCounter(T)
        void addOne() pure
                atomicOp!"+="(_v, 1);

        int getValue() const pure
                return atomicLoad(_v);

        T _v;

        shared test_counter = AtomicCounter!int(42);
        assert (test_counter.getValue() == 42);
        assert (test_counter.getValue() == 43);

shared counter = AtomicCounter!int(1);

And here’s the C code:

#include <stdio.h>

int count();  // From the D code

int main()
    int j;
    for (j = 0; j < 10; j++)
        printf("%d\n", count());
    return 0;

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):

$ dmd --version
DMD64 D Compiler v2.076.0-b2-dirty
Copyright (c) 1999-2017 by Digital Mars written by Walter Bright
$ ls
count.d  program.c
$ dmd -c -betterC count.d 
$ gcc count.o program.c -o program
count.o:(.data.DW.ref.__dmd_personality_v0+0x0): undefined reference to `__dmd_personality_v0'
collect2: error: ld returned 1 exit status

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):

$ objcopy -R .data.DW.ref.__dmd_personality_v0 -R .eh_frame count.o
$ gcc count.o program.c -o program
$ ./program 

It might not look like much, but it’s a huge improvement. Thanks to all the developers who helped make it happen.