I achieved a poorly documented thing where showing the commit to people actually helps them. To be more helpful, I now write a blog about it. May it serve as some documentation.
Quick overview
Our work will be in the clang and llvm project only, though you might want to add support to libc++.
LLVM only needs minor changes, 3 additional lines for LF OS. You have to add the OS to the Triple
enum
(llvm/include/llvm/ADT/Triple.h
) and detection + toString code for it in llvm/lib/Support/Triple.cpp
.
For clang we write a new TargetInfo
(this one sets the default preprocessor definitions) and a ToolChain
(this sets default flags, which linker to use, …). We also add some Code in the Driver
to use our new
ToolChain
.
Be aware of the long compile time of clang! You want to use ccache
. Also: a clang debug build is quite large.
50GB kind of large with a 2GB clang binary (dynamically linked! not sure, maybe it was actually statically
linked). You probably want to use the release config.
Diving in
The changes in llvm are pretty boring and described in the overview already. If the instructions are not enough, the rest of the process might be over your current abilities. Learn some more stuff an come back :)
Creating our new Target
means creating a new template in clang/lib/Basic/Targets/OSTargets.h
. I suggest to
copy an existing one and modify it for your needs. The most important stuff are the preprocessor definitions
you definitely need for building real stuff for your OS.
When your new Target
is added, you have to tell clang to use it in clang/lib/Basic/Targets.cpp
. Look for
the correct place in the switch-case-cascade to add your OS Triple and return your target (templated with the
CPU arch).
You added your target? Great! Now to the bigger chunk of code, the ToolChain
. A ToolChain in clang is a
combination of compiler, assembler, linker and stuff… the toolchain ¯\_(ツ)_/¯
To create your own ToolChain, create a new class in clang/lib/Driver/ToolChains
(I called mine LFOS). You
need a .h
and a .cpp
file. You can copy an existing one that matches your OS a bit (I used Fuchsia a lot)
or use the LF OS commit linked below as a reference.
The basic idea is to set the correct flags to find headers and libraries, add some static libraries if required, select a runtime (libgcc or compiler-rt), select a C++ stdlib implementation (libstdc++ or libc++) and so on.
After you have created your ToolChain
, you have to tell clang when to use it. Go into
clang/lib/Driver/Driver.cpp
and add an include for your toolchain header. Also add a new case in getToolChain
for your OS.
For LF OS I had to add some more cases in clang/lib/Frontend/InitHeaderSearch.cpp:AddDefaultCIncludePaths
to
tell clang not to use random additional default directories. I just added LF OS whereever I found Fuchsia
¯\_(ツ)_/¯
Side note: if you have problems with a dash in your OS name which you have replaced by an underscore in the
triple and clang not finding the compiler-rt or something: add a case to
clang/lib/Driver/ToolChain.cpp:getOSLibName
. See the LF OS commit for reference.
Recap
Define your OS triple. I use x86_64-pc-lf_os
. Make LLVM know about the OS component. Add a target in
OSTarget.h
and a Toolchain in clang/lib/Driver/ToolChains
. Make the Driver
know about your toolchain.
Maybe tweak some more random things.
I was surprised how fast I was able to find the things I have to change. The codebase is very clean, way cleaner than I expected for a compiler. The community is friendly, a fix in libc++ was appreciated and merged, the discussion helped me learn things (having my code reviewed by a member of the C++ standards committee was a bit intimidating ^^’).
A great new feature for clang would be to load a new operating system from a config file, maybe from sysroot. You’d never have to change clang for a hobby OS then as you could just write that config (clang is always able to cross-compiler to whatever target you may find).
Links
Copying
Feel free to copy, remix, share. Attribution would be nice but is not required.