llgo update: Go1, automated tests

This week I finished up Udacity CS373: Programming a Robotic Car, and also finally finished reading GEB. So I’ll hopefully be able to commit some more time to llgo again.


I moved on to Go’s weekly builds a while back, and updated both llgo and gollvm to conform. I’m now on Go 1, as I hope most people are by now, and llgo is in good shape for Go 1 too. That’s not to say that it compiles all of the Go 1 language, just that it runs in Go 1. Apart from that, I’ve just been working through some sample programs to increase the compiler’s capability.

One of the things that I’ve been a bit lazy about with llgo is automated testing, something I’m usually pretty keen on. I’ve grown anxious over regressions as time has gone on in the development, so I’ve spent a little bit of time this week putting together an automated test suite, which I mentioned in golang-nuts a few days ago. The test suite doesn’t cover a great deal yet, but it has picked up a couple of bugs already.

One of the numerous things I like about Go is its well integrated tooling. For testing, Go provides the testing package, and go test tool. So you write your unit tests according to the specifications in the “testing” package, run “go test”, and your tests are all run. This is comparable to, say, Python, which has a similar “unittest” package. It is vastly more friendly than the various C++ unit test frameworks; that’s in large part due to the way the Go language is designed, particularly with regard to how it fits into build systems and is parsed.

In Go, everything you need to build a package is in the source (assuming you use the “go” command).
  • The only external influences on the build process (environment variables GOOS, GOARCH, GOROOT, etc.) apply to the entire build procedure, not to single compilation units. Each variant will end up in a separate location when built: ${GOPATH}/pkg/${GOOS}_${GOARCH}/<pkgname>.
  • Platform-specific code is separated into multiple files (xxx_linux.go, xxx_windows.go, …), and they’re automatically matched with the OS/architecture by the “go” command.
  • Package dependencies are automatically and unambiguously resolved. Compare this with C/C++ headers, which might come from anywhere in the preprocessor’s include path.
So anyway, back to llgo’s testing. It works just like this: I’ve created a separate program for each test case in the llgo/llgo/testdata directory. Each of these programs corresponds to a test case written against the “testing” package, which does the following:
  1. Run the program using “go run”, and store the output.
  2. Redirect stdout to a pipe, and run a goroutine to capture the output to a string.
  3. Compile the program using llgo’s Compile API, and then interpret the resultant bitcode using gollvm’s ExecutionEngine API.
  4. Restore the original stdout, and compare the output with that of the original “go run”.
Pretty obvious I guess, but I was happy with how easy it was to do. Defer made the job of redirecting, restoring and closing file descriptors pain free; the go statement and channels made capturing and communicating the resulting data a cinch.

This is getting a little ramble-ish, so I’ll finish up. While testing, I discovered a problem with the way LLVM types are generated from types.Type’s, which basically means that they need to be cached and reused, rather that generated afresh each time. At the same time I intend to remove all references to LLVM from my clone of the “types” package, and offer my updates back to the Go team. It’s not fully functional yet, but there’s at least a few gaps that I’ve filled in.

One last thing: LLVM 3.1 is due out May 14, so gollvm and llgo will no longer require LLVM from SVN. I really want to eliminate the dependency on llvm-config from the build of gollvm. I’m considering a dlopen/dlsym shim and removing the cgo dependency on LLVM. I’d be keen to hear some opinions, suggestions or alternatives.

Until next time.

Posted April 8, 2012. Tags: go, llgo, gollvm, llvm.

Imports in llgo, jr.

So I realised I’m a doofus the other day, when I started getting closer to completion on producing export metadata in llgo. Rolling my own import mechanism is unnecessary for now. Instead, I can just lean on the import mechanism that exists in the standard library (well, until Go 1 at least): go/types/GcImporter.

I’ve modified llgo to use go/ast/NewPackage, rather than the old code I had that was using go/parser/ParseFiles. The NewPackage function takes an optional “importer” object which will be used for inter-package dependency resolution, whereas ParseFiles does no resolution. The standard GcImporter type may be used to identify exports by interrogating the object and archive files in $GOROOT. The AST that’s generated is filled in with external declarations, so it’s then up to llgo to convert those into LLVM external declarations. Easy peasy.

Now it’s time to come up with a symbol naming scheme. Without having thought about it too hard, I’m going to start off with the assumption that the absolute name of the symbol (package+name), with slashes converted to dots, will do the trick. Once I’ve implemented that, I’ll need to start work on the runtime in earnest. It’s also high time I put some automated tests in place, since things are starting to get a little stabler.

In the long term I’ll probably want to continue on with my original plan, which is to generate module-level metadata in the LLVM bitcode, and then extract this in a custom importer. It should be quite straightforward. Earlier this week I wrapped up some updates to gollvm to add an API to make generating source-level debugging metadata simpler. This will be useful not only for describing exports, but also for what it’s intended: generating DWARF debug information.

In other news: my wife just ordered the 1-4a box set of The Art of Computer Programming for me. At the moment I am slowly making way through Gödel, Escher, Bach: an Eternal Golden Braid, and so far, so good. Looking forward to more light reading for the bus/train!

Posted February 19, 2012. Tags: go, llgo, gollvm, llvm.