This Weekend in Meson++ (December 15th)

 It's been a while since I wrote about Meson++, or even worked on it, but I have made some changes since the last time I posted, and I thought it would be a good idea to write about them.

Rewriting How Basic Blocks and Instructions are iterated, twice.

 The two biggest changes are tightly coupled, and hard to talk about separately. The rewriting of the Basic Block iteration code and the rewriting of the Global Value Numbering pass (GVN). GVN is a critical piece of an SSA based compiler, it tags every variable read and write with a globally unique number. With this information (and with phi nodes), we can easily simplify the program, and eventually propagate raw values into function calls.

Our original block walking code had some issues. Those were correctness, and performance. On the correctness side it had two problems, the first is that it didn't necessarily walk the CFG (Control Flow Graph) such that a blocks predecessors were all visited before it was, despite the fact that the passes assumed this. The second issue was both a performance and correctness issue, in that our block walking code would often visit the same basic block twice in a single iteration. Fixing the second one proved interesting, as there was a case of rewriting a C++ container in an iterator. This would segfault when run normally, but passed with the Memory Sanitizer enabled. Fortunately, valgrind was able to detect the issue.

An additional fix here that had a huge performance impact was to rewrite our visitor code to visit nested instructions from the inside out, such as a function call like `find_program`. This means that we need fewer iterations of the program than we did previously. With these changes Meson++ and Muon (0.2.0) can resolve simple programs at roughly the same speed (Meson++ is a little slower, but I would expect it to be given the way it works). I don't expect this to last, as Muon is more feature complete than Meson++, but it's a nice feeling for the moment, haha.

 Control Flow

Speaking of control flow, that's been completely reworked too. The original design was that each basic block had an exit attached, which was either a pointer to another block, or what was essentially a ternary with two basic blocks and a condition to choose between them. This was okay, it worked. It had a couple of problems. For one, you couldn't exit a block early, like with `subdir_done()`. It also meant that if a block had multiple calls to subdir at the end, that you had to have N - 1 blocks to represent that. We now have two new instructions, Branch and Jump. Branch allows representing an if tree of subdirs as a single instruction, which can be simplified over time. Jump represents a move from one node on the CFG to another, and supports predication, which will allow us to simplify things like `if not thing; subdir_done()` to reduce the number of blocks.

vcs_tag

The last major feature implemented recently is support for vcs_tag. This was actually surprisingly difficult, requiring a lot of TODOs to be resolved in order to implement the function. I've diverged a bit from Meson here by implementing it as a runtime fuction rather than a compile time one, since it makes configure faster, but has relatively little impact on build time performance. I may have to roll this back since people probably don't correctly set up their dependencies to handle this.         

The Other Stuff

There's also lots of smaller features and cleanups that have happened along the way.

  • Basic support for the `test()` function and running unit tests
  • lots of bug fixes
  • compiler.get_id
  • disablers

Comments

Popular posts from this blog

This Weekend in Meson++ (February 6th)

Rust in Meson — 0.57 Edition

Introducing Meson++