RFC: Enable ClangIR Build By Default-

ninja. I now also tried ninja clang, but that didn’t make it better, the wall time is +155%:

main main+CIR increase main main+CIR increase
ninja ninja ninja clang ninja clang
CMake wall time 22.01 33.26 +51.1%
Build CPU time 8004.51 19010.74 +137.5% 6946.63 15945.48 +129.5%
Build wall time 113.20 262.03 +131.5% 95.71 243.70 +154.6%
max-rss 948304k 2935520k +209.6% 948860k 2939264k +209.8%
build dir size 2.0G 4.0G +100.4% 1.5G 3.0G +98.8%
Build edges (lost) 7377 3750 6059 +61.6%
CC=clang CXX=clang++ /usr/bin/time cmake -DLLVM_ENABLE_PROJECTS="llvm;clang" -DLLVM_TARGETS_TO_BUILD="X86" -DCMAKE_BUILD_TYPE=Release -G Ninja -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_USE_LINKER=lld -B llvm-build-tmp32 -S llvm-project/llvm && /usr/bin/time ninja -C llvm-build-tmp32 clang
CC=clang CXX=clang++ /usr/bin/time cmake -DLLVM_ENABLE_PROJECTS="llvm;clang;mlir" -DLLVM_TARGETS_TO_BUILD="X86" -DCMAKE_BUILD_TYPE=Release -G Ninja -DLLVM_ENABLE_ASSERTIONS=ON -DLLVM_LINK_LLVM_DYLIB=ON -DLLVM_USE_LINKER=lld -B llvm-build-tmp33 -S llvm-project/llvm -DCLANG_ENABLE_CIR=ON && /usr/bin/time ninja -C llvm-build-tmp33 clang

NB: building fewer things is great for CPU time, but on my 48c/96t machine I noticed the the wall-time dominating part seems to come from really slow CUs, e.g. CIR/CIRDialect.cpp.o 68s, LLVMIR/LLVMDialect.cpp.o 121s, LLVMIR/NVVMDialect.cpp.o 108s, LLVMIR/ROCDLDialect.cpp.o 134s, and it seems that at least some of these files are required. Optimization should also reduce the latency of these files.

stage2-O0-g +0.17%, stage2-O3 +0.05%, actually, just from building with ClangIR.

I also wanted to get stats (time, page-faults, etc.) on the time it takes to compile a simple C program, but the Clang-with-ClangIR crashed in the dylib build…

$ echo 'int main(){return 0;}' > hello.c && ./llvm-build-tmp33/bin/clang -c hello.c
Attribute list does not match Module context!
AttributeList[
  { function => noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
]
ptr @main
fatal error: error in backend: Broken module found, compilation aborted!
clang: error: clang frontend command failed with exit code 70 (use -v to see invocation)
clang version 24.0.0git (https://github.com/llvm/llvm-project.git f4ad12c3e4371874aaf40078785fb3d00a664aa0)
...
$ echo '' > empty.c && ./llvm-build-tmp33/bin/clang -c empty.c
Pass 'Pre-ISel Intrinsic Lowering' is not initialized.
Verify if there is a pass dependency cycle.
Required Passes:
        Target Transform Information
        Target Library Information
clang: /home/engelke/llvm-project/llvm/lib/IR/LegacyPassManager.cpp:706: void llvm::PMTopLevelManager::schedulePass(Pass *): Assertion `PI && "Expected required passes to be initialized"' failed.
clang: error: clang frontend command failed due to signal (use -v to see invocation)
clang version 24.0.0git (https://github.com/llvm/llvm-project.git f4ad12c3e4371874aaf40078785fb3d00a664aa0)

This did succeed, though: echo '' > empty.c && ./llvm-build-tmp33/bin/clang -emit-llvm -c empty.c -o /dev/null, so we have:

hyperfine -L n 32,33 './llvm-build-tmp{n}/bin/clang -emit-llvm -c empty.c -o /dev/null'
Benchmark 1: ./llvm-build-tmp32/bin/clang -emit-llvm -c empty.c -o /dev/null
  Time (mean ± σ):      15.4 ms ±   1.2 ms    [User: 5.3 ms, System: 10.0 ms]
  Range (min … max):    13.4 ms …  17.7 ms    180 runs
 
Benchmark 2: ./llvm-build-tmp33/bin/clang -emit-llvm -c empty.c -o /dev/null
  Time (mean ± σ):      26.4 ms ±   1.8 ms    [User: 12.9 ms, System: 13.5 ms]
  Range (min … max):    23.3 ms …  29.0 ms    108 runs
 
Summary
  ./llvm-build-tmp32/bin/clang -emit-llvm -c empty.c -o /dev/null ran
    1.72 ± 0.18 times faster than ./llvm-build-tmp33/bin/clang -emit-llvm -c empty.c -o /dev/null

Other stats: page-faults 2,400 → 3,394 (+41%), instructions 38,271,658 → 72,081,509 (+88%), max-rss 77208k → 112812k (+46%). There are some dylib-only costs in here, but most distros build Clang/LLVM as dylibs, so that’s what most users will get. So there’s certainly a significant cost that comes from just shipping Clang-with-ClangIR for all users (even if this experiment is synthetic and amplifies the increase).

… but also a lot less general.

I’d rather say: this is the sales-pitch of any well-designed IR. I consider MLIR to be a not-particularly-good implementation of that idea and this thread shows some of MLIR’s key downsides. Maybe ClangIR would’ve been better off from not using MLIR – but let’s not derail this thread into “should ClangIR use MLIR”, philosophical-ideological discussions tend to lead nowhere.

4 Likes