Hacker News new | past | comments | ask | show | jobs | submit login
Hacking on Clang is surprisingly easy (mort.coffee)
134 points by mort96 25 days ago | hide | past | web | favorite | 26 comments



The article mentions debug build time as a legitimate pain point about the LLVM project. General good advice, which unfortunately isn't enabled by default: use dynamic linking, make sure you're using lld, enable split dwarfs (and -Wl,--gdb-index for faster gdb load times!), enable optimized tablegen[0]. ccache doesn't hurt but isn't actually such a big benefit unless you switch between branches a lot.

Oh, and buy a Threadripper. Seriously, you won't regret it.

[0] TableGen is LLVM's internal DSL swiss army knife. You can separately enable optimizations for TableGen independently of optimizations in the resulting LLVM builds, which you should basically always do unless you're working on TableGen itself.


It's not the compilation that kills you, it's the sudden link at the end. Linking requires so much RAM it routinely succumbs to the OOM killer.

Do you have a trick to link serially? I usually just do a `ninja -j 1` after the main build has failed.


You can use LLVM_PARALLEL_LINK_JOBS.


Thanks!


Do you know why isn't it easier to offload compilation to the cloud yet? Compilation is an embarrassingly parallel problem and shouldn't need take longer than than slowest file plus linking. Compilation is also a pure function (I know it's not quite set up like that in practice but it is in theory) so it's stateless and cacheable. I would have thought developers could share a massive cluster and have thousand-way compilation instantly. Doesn't seem to be a thing people are building or using in practice for some reason.


Developers at my company who work on a large shared C++ codebase all use Icecream[0] to distribute the builds across their workstations. I've only hooked into it to build once since I don't work in that codebase, but it seems to work pretty well scaling it (I think on the order of 50-60 servers and about the same number of clients). As for caching, I think sccache[1] is designed to handle that in a scalable way, although I've personally only used it locally for my Rust builds.

[0]: https://github.com/icecc/icecream [1]: https://github.com/mozilla/sccache


I nearly have this working, using AWS Lambda (a few months ago I got super angry at the compile times for a project I was working on and spent a week getting a proof of concept ready that I have been refining since). Have sadly been busy with some other tasks I have to get done (age old story), but intend to push to the point where you could easily add it to your own project soon (as long as your build system isn't stupid; a lot of people think they are using super advanced build systems, but in practice they handle toolchain hacks extremely poorly :/).


If I was writing a new compiler from scratch today I think I'd make it client-server from the start, even locally, so that the compiler could be transparently made remote. The client would be responsible for doing all IO on behalf of the server so that the server was completely separated from the system it ran on.


https://github.com/StanfordSNR/gg can do builds in the cloud. Their USENIX talk is really interesting


distcc does this for C and C++: https://distcc.github.io/


That's good to know, and I'm not surprised that there are ways to speed up the build. I just found that enabling release mode made linking reasonably fast and didn't spend more time on it (and experimenting more would involve more complete recompiles :p)

Threadrippers seem really nice, but I did this on my laptop, not a desktop. Plus, when linking with debug symbols, my 16GB of RAM is by far a bigger limit than my four cores; I can't even run two link processes simultaneously without one of them being OOM killed (if I'm lucky).


Hey! I'm starting my master's in the field of compilers.

Could maybe could you share your setup/workflow? For example do you use any IDEs? I'm trying to use Clion but it seems 16GB of RAM is not enough.

Any tips would be appreciated :)


> Oh, and buy a Threadripper. Seriously, you won't regret it.

How much difference are we talking? Do you have a concrete Intel CPU you can compare against to give ballpark numbers? And is this with various mitigations enabled or disabled?


Phoronix usually has pretty comprehensive benchmarks for compilation-heavy tasks:

Threadripper 3960x & 3970x from Nov 2019:

https://www.phoronix.com/scan.php?page=article&item=amd-linu...

^ <2min to compile LLVM 6

Core i9 9900K from Oct 2018:

https://www.phoronix.com/scan.php?page=article&item=intel-co...

^ here’s another, to give you an idea of how things have changed in the last ~year or so


We have Ryzen 9 3900X workstations at work and I am using a 3950X at home. That's already a huge boost to compile times and they cost way less than the threadrippers


The table seems tell a different story. I guess not including cost as a factor in that benchmark ?

For the article funny it refers to lisp.


Oh awesome, thank you!


Really cool read. One thing which I was curious about, but is not completely related to the article, is how the workflow of making changes to a big project like Clang looks like. Is there some « watch » mode to iterate faster without needing to rebuild? What kind of tooling/editor is recommended, is there some linting support? Is it possible to quickly identify a subset of tests to run for the changes made? (In this case I assume there are tests for lambda parsing?).


My workflow was extremely simple; a terminal window, with cutting-edge tools like ripgrep to search for patterns and neovim to edit. When I had made a change, I ran make.

When I wanted to test out a change, I had print statements in the clang source code and wrote a small test file which I could use to decide whether the prints I saw matched the prints I expected to see. Revolutionary stuff, I know. One trick I like when working with huge projects is to do my printf debugging with `fprintf(stderr, ...)` instead of the project's standard logging, just to avoid having to fight with log levels and such. (These print statements should obviously be removed once they're no longer necessary; here again, using fprintf helps, because they stand out.)

I didn't even think about tests, but I'm happy to report now that the entire test suite still works after my change. I don't think any kind of test-driven debugging would have worked here; almost none of the challenge was in the actual code behind the feature, it was all mostly just work to try to understand the code base. For example, "Oh, this looks like it's where lambdas are parsed. Let me add a print here and try to compile a file with a lambda to see if I'm right", or "Ok, I think I should end up in this branch if I have a fat arrow token in a lambda expression, let's add a print statement and modify the test file to check if that's true".


The llvm hacking guide has answers to some of these questions: https://clang.llvm.org/hacking.html There is more information in the llvm docs as well.

I don't know about a watch mode, but I'm not sure how much that would save you as you'd constantly be relinking a very large project. It might even be counter productive. As for tooling and editors, the build system is well integrated with visual studio on Windows, and I think xcode on mac os. For linting the llvm project has clang-format and clang-tidy which are excellent tools for any C++ project. I believe there is an extensive system test suite written in Python described in the hacking docs.


> Is it possible to quickly identify a subset of tests to run for the changes made?

Bazel makes that simple for C++ projects.


The simplified syntax looks pretty much like the C# syntax.

https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...


  [](auto &&a, auto &&b) { return a.id() < b.id(); };
I vary much prefer the version above than the one author proposes for replacement. C++ is a beast as it is. No reason to introduce more miracles there. My opinion of course


I dunno, it's kinda consistent. Braces are optional for single expression bodied if statements too. It doesn't really seem all that strange for them to be optional for single expression lambdas.


You could also use libclang to parse and codegen the extra braces.

https://clang.llvm.org/docs/Tooling.html

That way you don't have to maintain your own branch of clang.


That's why I like hacker news so much, you can stumble upon an article answering many questions you asked yourself since forever, great article!




Applications are open for YC Summer 2020

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: