Project Quagmire

This weekend I finally registered project quagmire with Google’s code hosting service. I’ve checked in all my initial .mk files, plus a little bit of documentation.

I’m a little nervous about posting this… Quagmire is still very immature, and I don’t want expectations set too high. There is still a lot to do! But, the good news is, you can easily help set the direction.

I suppose I will set up a Google group for Quagmire eventually. What do you think? Meanwhile, edit the Quagmire wiki with your questions, comments, and ideas.

Emacs Tip: gtk-look.el

One thing I like about Emacs is that when I ask for information, the default case is that it is displayed in a section (which Emacs calls a “window”) of the window (which Emacs calls a “frame”) that I’m already focused on. What’s nice about this is that if I ask for, say, help on a symbol in C mode, the information will show up, but I don’t have to look away, or worry about a new window stealing the focus, or move the mouse around. Also, I can easily dismiss the display without moving my hands from the keyboard.

Do you know about help on symbol in C mode? I didn’t know about this until last week. If you are in a C mode buffer, you can type C-h S, aka info-lookup-symbol, to look up help for a given C symbol. This works by digging through the libc info pages… this isn’t perfect, but it is pretty handy. (And, yes, I’m one of the few people who really loves info and uses it every day. I wish all the documentation on my system were available in this format — plus one or two others, yet to be invented, which would allow better IDE-ish features in Emacs.)

On a related note, a couple days Kevin Ryde sent me the latest version of gtk-look.el for uploading into ELPA. I’ve been meaning to try this for a while, so I finally installed it. gtk-lookup-symbol is similar to info-lookup-symbol, except it uses the devhelp documentation libraries as its source. Great stuff!

Naturally, I could just use devhelp, which is a very nice tool. Unfortunately, it is a very nice tool with one major drawback — it makes a new window, with all the attendant problems. And, by default, gtk-look suffers from this problem as well; the docs show up in firefox.

Luckily, Emacs is absurdly configurable, and this problem is solved by installing the w3m-el package (on Fedora) and configuring browse-url-browser-function to display devhelp files in w3m:



(setq browse-url-browser-function

    '(("file:.*/usr/local/share/gtk-doc/html" . w3m-browse-url)

     ("file:.*/usr/share/gtk-doc/html" . w3m-browse-url)

     ("." . browse-url-firefox)))

With this I can easily view the help for gtk, glib, etc, functions in the Emacs-y way that I prefer. If this sounds good to you, you’re only seconds away from installing it via ELPA.

Now what I need is c-lookup-symbol-dwim, which searches all the databases. This shouldn’t be too hard…

Liver

LiverA couple weeks ago we had to euthanize our dog Liver. She had a brain tumor and was in a lot of distress.

As those of you who met her will know, Liver was a high-strung and difficult dog; it’s appropriate that she’s wearing her muzzle in the only photo I have to hand.

Now, though, is not the time to discuss Liver’s deficiencies. Despite her fears and consequent overuse of the fang, she had many of the qualities that bind people so closely to their dogs: enthusiasm, boundless joie de vive, eagerness to please, and unquestioning loyalty. Like many dalmatians, she was a clown (if often by mistake); and she is the only dog I’ve met who would come give me attention if I was crying — as if she understood.

So, rest in peace Liver. We miss you.

PS I Love You

This was billed as a romantic comedy and, knowing nothing else about it, we went to see it over the holidays. I don’t think I’ve ever cried this much during a comedy, not even Talladega Nights. It really wasn’t what I was expecting at all… I was in the mood for light escapism.

Not quite as bad as when the video clerk told us that Donnie Darko was a funny movie about a teenage superhero, but bad enough.

Hunk Boundaries

How do we decide the boundaries of reuse in the incremental compiler?

The one crucial thing when looking at reuse is that the signature that you choose must be stable across compilations. And, it is better if the boundaries are cheap to compute and roughly line up with declaration boundaries. In the prototype implementation, I accomplished these by using file change events (conveniently noted by libcpp) as hunk boundaries. File change boundaries are roughly stable (moreso for system headers), are free (libcpp already computes them), and very frequently align with declaration boundaries. This last criterion is typically only broken by constructs where a file is included in the middle of a declaration — for instance, in gcc itself by including a .def file in the middle of an enum, to populate it. I added a special case to the reuse code to account for these situations.

Aside from the convenience — which is very important for prototypes, but much less so for production — there was a historical reason that I chose file boundaries. When I first started thinking about writing an incremental compiler, I focused primarily on header files, and on having a single model which would encompass both the preprocessor and the compiler. After working through a number of cases, with the help of the prototype, I decided that this was “java thinking”; in C the primary unit of reuse is the declaration — not the file. And, it is much, much simpler to reason about C if you look at it after preprocessing.

In earlier posts I described the dependency computation that is needed for sharing declarations. One consequence of this is that the bigger the unit of reuse, the less often it will actually be reusable. At one extreme is gcc’s current PCH implementation, which requires a single header file to be included first in all compilations. With the file-boundary scheme, it turns out that even relatively ordinary programs fail to reuse in many situations. For instance, some headers in gcc have conditional declarations — so, change the order of includes, and an entire header’s worth of declarations must be re-parsed.

Note that failure to reuse is not a bug. Reuse is, after all, a performance and memory optimization. The worst consequence is that gcc will run as slowly, and will use as much memory, as it does now. Still, reuse is desirable. The more we reuse, and the smaller our units of reuse, the less work we have to do when recompiling.

So, this leads to the second implementation, which tries to guess declaration boundaries without parsing. This fast, if somewhat evil-looking, and it yields good results. This still isn’t perfect, though (it does not handle K&R-style definitions properly), so I will probably rewrite it again to do something closer to a quick parse.

I’m not sure whether this same approach makes sense for C++. I assume I will start here, because I understand it and know that it will work at least minimally. But, C++ brings its own problems. For instance, suppose you have an inline function defined in a class definition. In this case, if you change the body of the function, you’d like to be able to reuse the class definition, if possible — otherwise, many kinds of small changes will yield costly compilations. Tom Fitzsimmons suggested that perhaps the hunk computation can be made recursive in some way, meaning that perhaps it could operate inside of classes and namespaces, and not just at the top level. I’ll definitely be researching this once I start on the C++ front end.

The split between front and back end incremental processing means that each front end is free to implement its own approach to incrementality. I’m only looking at C and C++, but other people are free to hack on the other front ends. The actual changes to the middle-end interface are quite small, as it turns out.

What next? I think future posts will cover scalability, multi-processor support, and perhaps what to do about the C preprocessor. If there’s anything you want to know more about, post a comment and I will address it.

Incremental Code Generation

In an earlier post I said that I’d eventually explain how incremental code generation works.

First, to recap: I’ve already explained how incremental compilation works in the front end. Stopping here would still yield some speedups — I regularly see 20% compile-time improvements on ordinary programs; sometimes much larger; and for C++ I expect the effect will be bigger.

But, for many compilations, particularly when compiling C using optimization, it is the back end which is the major time sink. And since we already know from the front end which functions have changed (i.e., have been re-parsed) and which have not, it seems like it should be easy to also run the back-end incrementally.

There are a few plausible ways to implement this. Essentially, we can preserve the compiled form of the program at any point in the pipeline. By choosing a point after most of the work is done, we can minimize the time spent needlessly recompiling.

My current plan is to do the work at the very latest point possible, by writing out object files. The idea here is that each top-level definition in a translation unit — each global variable or function — will get its own tiny object file. Then, these files will be linked together to form the object file that the user actually asked for.

Of course, the current linker is slow and not at all suitable for this sort of thing. So, we’ll use a new, fast, incremental linker — which we’ll need anyway, in order to preserve incrementality through the entire compile cycle. (It isn’t much good to have a fast incremental compiler if you still end up waiting for the linker all the time.)

This approach has the nice property that the gcc work is relatively easy, for the most part. I don’t know yet whether this outweighs the work that must be done elsewhere, so I’m going to implement a prototype and find out. In fact, this is the next major milestone (though it will be a while, because I’m in a bug-fixing phase).

Another fun possibility here is that we can try to be smart about managing the cache of small object files. In particular, we may be able to preserve some benefits of incrementality even across restarts of the server. For C++, this could include template instantiations.

One drawback of the plan is that it may make dealing with debug info somewhat tricky. This ties in to how we compute hunks; if we don’t include source location in the hunk checksum, then we can end up with objects that lie to the debugger about where their corresponding source appears. We’d want to fix that up somehow, but that is not simple. I’m still thinking about this particular sub-problem.

Another oddity is handling inlining. In my planned implementation, gcc would fork and run code generation in a subprocess. (This lets me avoid excessive modifications to gcc.) But, if we inline a function then the compile server needs to know about this in order to properly re-compile when the inlined function is changed.

Future posts should include information about computing hunk boundaries (pretty short, but maybe I’ll touch on ideas for C++), multi-processor support, and maybe some speculation about scalability.

Replacing Automake

Inspired by Ian, this past weekend I took a look at replacing the auto tools with GNU make code.

Replacing automake turns out to be very easy. And, given that GNU make is everywhere now, I think it is well past time to do this.

It took about 250 lines of code to get C and C++ compilation — with dependency tracking — install, clean, libraries, and programs working.

Of course, this is just the beginning. There’s still shared libraries (ugh), dist, distcheck, and a host of smaller things.

Still, the result is quite nice. The resulting user Makefiles are pretty similar — but, unfortunately, not identical. With a bit of help it could be ready for real use by Christmas. Write me if you’re interested.

The long term goal, of course, is to unify all three tools. I’ve also got some prototype GNU make code to do some configure-style checking, based on some earlier prototyping by Ben Elliston. This is less fully baked, and there are some problems I haven’t solved. But, I think the idea is sound.

In my view the plan would be to finish automake and libtool functionality in the new code, then move on to adding configuration support. The latter can be done incrementally. In the end, we will have replaced all the tools with something better, without undue disruption.

The most important decision — what to name it — is still open. Submit your ideas.

Anti-dependencies

Last week I implemented anti-dependencies in the C front end. This wasn’t difficult enough to write about, but it provides an excuse to describe dependencies and incrementalism in general.

The fundamental idea in speeding up the front end is that it should avoid parsing whenever possible. If we’ve already parsed a function definition in some earlier compilation, it is faster to reuse that definition than to try to parse it again. (At least, that is true if noticing that we can reuse it is faster than parsing. But it always is. Note also that this will be a much bigger win for C++ than for C.)

Reuse works by computing a checksum of a run of tokens (one of these runs is called a “hunk” in the implementation, a name that vaguely embarrasses me). If we’ve seen the checksum before, we reuse the parsed results, which we store in a table.

Of course, the situation is more complicated than that. A bit of code may depend on other bits of code, for instance:

typedef int myint;
extern myint variable;

Here we can’t reuse variable in isolation — we can only reuse it if we’ve already reused the previous line. So, each hunk carries with it some dependency information. Before last week, this was just a list of all the checksums of hunks on which the current hunk depends.

Even this dependency tracking is not enough for proper operation. Suppose we already compiled this program:

struct x;
struct x { int field; };

… and then try to compile:

struct x;
struct x { int bug; };
struct x { int field; };

With a naive dependency scheme, this would compile without error, because we would erroneously reuse the second definition. After all, all its dependencies have been met.

Anti-dependencies are the fix for this. When considering a hunk for reuse, we check not only that its dependencies have been met, but we check that any declarations that we might reuse have the correct “prior value”. In fact, these two checks amount to the same thing; rather than record the checksums of prerequisite hunks, now we simply record declarations.

Given all this, how will incremental compilation work? My current plan is not to introduce any sort of special case. Instead, the parser will notify the backend when it parses a function. A function which comes from a reused hunk will simply not be compiled again.

If you think about it a bit, you’ll see that the prerequisites form a dependency model of a compilation unit. If the developer changes a declaration, anything using that declaration will be invalidated, and this invalidation will itself propagate outward. So, the amount of recompilation will be proportional to the “importance” of the change. Change a central typedef, and many things may need recompilation. Change a comment, and nothing will (though handling debug info properly may be a challenge).

A future refinement is the idea of a “semantic dependency”. The idea here is that, rather than check prerequisite declarations by identity, check them by ABI compatibility. There are a few tricks here, so this is a bit lower on my priority list for the moment.

You might have wondered how we compute the boundaries of a hunk, or how we can actually avoid recompiling functions. I’ll answer both of these in future posts.

Fedora 8

Anthony was in town this past Thursday, and after talking to him I was inspired to follow his example and upgrade to Fedora 8. Like him, I did a live upgrade; but I only upgraded my laptop, which was running Fedora 7. My main machine still runs 6… I’m more cautious about upgrading it, but after playing with Fedora 8 a bit, I can feel the pressure rising.

The biggest change I’ve noticed so far is the inclusion of IcedTea. I’m happily using the browser plugin for the all-important chess applet.

Speaking of which… I’ve been looking at other brower plugins lately. Any reports on the flash support in Fedora 8? I’m using the (boo hiss) proprietary flash plugin right now. Also, any experiences with Firebug or Adblock?

I’m impressed with the Fedora maintainers. They manage to release a solid operating system every six months… not an easy task. Upgrades for me have always gone pretty smoothly; I’ve done a mix of upgrades from CD and yum upgrades, and haven’t been burnt (a couple minor singes, but always known bugs with simple fixes). Also, each release has had some compelling benefit making me want to upgrade.