一个hello-world程序是如何被clang/llvm编译的

Table of Contents

这篇文章是我学习clang/llvm的一个总结。

clang的架构

编译原理课程上会讲,一个编译器大致分为分为词法分析、语法分析、语义分析、中间代码生成、目标代码生成这些阶段,其中贯穿了错误处理和符号表处理。

clang/llvm在实现上是跟课本里讲的东西有区别的。例如,C/C++语言有预处理这一步,)例如 #pragma ,clang中把lexer集成到了preprocessor,词法分析时,preprocessor调用lex,普通的输入流由lexer处理,而lexer遇到宏展开(#define xxx yyy),头文件解析(#include<fmt/format.h>)或者编译器原语(#pragma)时,lexer返回给preprocessor处理。

https://raw.githubusercontent.com/VitalyAnkh/img/main/images/Pictures/screenshots/2022-03-02-19-51-57-photo_2022-03-02_19-51-35-cd2e.jpg

编译器驱动器(clang driver) clang 和编译器前端

首先要区分编译器驱动器和编译器前端库。前者负责生成编译任务的配置、调度编译任务、运行汇编器和链接器,也就是说,它驱动整个编译过程的进行。而后者是有具体功能(预处理、词法分析、语法分析、语义分析等)的库,也指那个可执行文件 cc1 。在这篇文章里,编译器前端库用clang指代,整个编译器工具链用clang/llvm指代,而这个驱动程序用小写的代码字体 clang 表示, clang 调用的具体执行任务的编译器用 cc1 表示。

一个简单的 hello-world.cpp 程序是如何被clang/llvm编译的

讨论的编译器版本和平台信息:

$ clang --version
clang version 13.0.1
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin

编译的前端 clang

我们来用经典的 hello-world.cpp 来探究clang是如何把它编译成一个可执行文件的,具体到每个Clang中函数的调用。

#include <fmt/format.h>
int main(){
  fmt::print("Hello world!\n");
  return 0;
}
Hello world!

来尝鲜一下实现了 C++ 20std::foramt 标准 的 fmt 库。为了让clang找到并链接这个库,我们要喂给 clang 一个flag: -lfmt ,让它可以链接到这个库。当然要想正常运行,你的系统上要先在正确的位置有这么一个库,Linux系统上就是在 /usr/include/fmt )。

-lfmt 是传递给 clang 的命令行参数。 clang 从命令行参数和预定义的平台相关选项生成编译配置。 -lxxx 表示要链接 xxx.

clang/llvm那么多组件让人毫无头绪,如何从中分析出 clang 编译一个程序时函数间的调用关系呢?

抛开这个 hello-world.cpp 不谈,我们写一个让clang崩溃的cpp文件,看看函数的调用栈。 这个cpp文件如下:

struct typeBase
{
  constexpr typeBase() : Lvalue() {}
  int Lvalue;
};

struct type : private typeBase
{
  bool value;
  type(bool v = 0) { value = v; }
  consteval type(int i) { value = i; }
  const bool operator*() const { return value; }
};

auto operator > (const type&a, const type&b) { return *a > *b; }

int main() {
  type T;
  if (T > type(100)) { }
}

我使用的llvm-project的commit hash是1f971e23f089c640d5a7df1e78572fe4d8bb1d0b,提交时间是2022年2月28日14:25。要想获得查看同样的调用栈,确保使用相同的commit. 调用栈如下:

#64 0x0000557422bab7da _start (/opt/compiler-explorer/clang-trunk/bin/clang+++0x11ab7da)
#63 0x00007fc70c4b30b3 __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b3)
#62 0x0000557422abb752 main (/opt/compiler-explorer/clang-trunk/bin/clang+++0x10bb752)
#61 0x0000557425780405 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*> >&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3d80405)
#60 0x0000557425777a3f clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command const*> >&) const (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3d77a3f)
#59 0x0000557425776efa clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const*&) const (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3d76efa)
#58 0x00005574257a3ac8 clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, bool*) const (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3da3ac8)
#57 0x0000557424ef34c3 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x34f34c3)
#56 0x00005574257a19f5 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*, bool*) const::'lambda'()>(long) Job.cpp:0:0
#55 0x0000557422babc5d ExecuteCC1Tool(llvm::SmallVectorImpl<char const*>&) driver.cpp:0:0
#54 0x0000557422bafca4 cc1_main(llvm::ArrayRef<char const*>, char const*, void*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x11afca4)
#53 0x0000557425a37013 clang::ExecuteCompilerInvocation(clang::CompilerInstance*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x4037013)
#52 0x00005574259053e2 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3f053e2)
#51 0x000055742596ac41 clang::FrontendAction::Execute() (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3f6ac41)
#50 0x0000557425f6e1f2 clang::CodeGenAction::ExecuteAction() (/opt/compiler-explorer/clang-trunk/bin/clang+++0x456e1f2)
#49 0x0000557426de41cc clang::ParseAST(clang::Sema&, bool, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x53e41cc)
#48 0x0000557425f64ef2 clang::BackendConsumer::HandleTopLevelDecl(clang::DeclGroupRef) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x4564ef2)
#47 0x0000557425f70251 (anonymous namespace)::CodeGeneratorImpl::HandleTopLevelDecl(clang::DeclGroupRef) ModuleBuilder.cpp:0:0
#46 0x000055742541cdb1 clang::CodeGen::CodeGenModule::EmitTopLevelDecl(clang::Decl*) (.part.5387) CodeGenModule.cpp:0:0
#45 0x0000557425416a13 clang::CodeGen::CodeGenModule::EmitGlobal(clang::GlobalDecl) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3a16a13)
#44 0x00005574254162d5 clang::CodeGen::CodeGenModule::EmitGlobalDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3a162d5)
#43 0x0000557425419198 clang::CodeGen::CodeGenModule::EmitGlobalFunctionDefinition(clang::GlobalDecl, llvm::GlobalValue*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3a19198)
#42 0x00005574253d273a clang::CodeGen::CodeGenFunction::GenerateCode(clang::GlobalDecl, llvm::Function*, clang::CodeGen::CGFunctionInfo const&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x39d273a)
#41 0x00005574253c8c43 clang::CodeGen::CodeGenFunction::EmitFunctionBody(clang::Stmt const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x39c8c43)
#40 0x000055742537852c clang::CodeGen::CodeGenFunction::EmitCompoundStmtWithoutScope(clang::CompoundStmt const&, bool, clang::CodeGen::AggValueSlot) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x397852c)
#39 0x0000557425372ad9 clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt const*, llvm::ArrayRef<clang::Attr const*>) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3972ad9)
#38 0x000055742537820e clang::CodeGen::CodeGenFunction::EmitIfStmt(clang::IfStmt const&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x397820e)
#37 0x0000557425372ad9 clang::CodeGen::CodeGenFunction::EmitStmt(clang::Stmt const*, llvm::ArrayRef<clang::Attr const*>) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3972ad9)
#36 0x00005574253780d1 clang::CodeGen::CodeGenFunction::EmitIfStmt(clang::IfStmt const&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x39780d1)
#35 0x00005574253c7fb8 clang::CodeGen::CodeGenFunction::EmitBranchOnBoolExpr(clang::Expr const*, llvm::BasicBlock*, llvm::BasicBlock*, unsigned long, clang::Stmt::Likelihood) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x39c7fb8)
#34 0x0000557425635315 clang::CodeGen::CodeGenFunction::EvaluateExprAsBool(clang::Expr const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c35315)
#33 0x000055742567c2a3 clang::CodeGen::CodeGenFunction::EmitScalarExpr(clang::Expr const*, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c7c2a3)
#32 0x000055742567aa98 (anonymous namespace)::ScalarExprEmitter::Visit(clang::Expr*) CGExprScalar.cpp:0:0
#31 0x0000557425679bdc (anonymous namespace)::ScalarExprEmitter::Visit(clang::Expr*) CGExprScalar.cpp:0:0
#30 0x0000557425679bf3 (anonymous namespace)::ScalarExprEmitter::Visit(clang::Expr*) CGExprScalar.cpp:0:0
#29 0x0000557425685250 (anonymous namespace)::ScalarExprEmitter::VisitCallExpr(clang::CallExpr const*) CGExprScalar.cpp:0:0
#28 0x0000557425648ae8 clang::CodeGen::CodeGenFunction::EmitCallExpr(clang::CallExpr const*, clang::CodeGen::ReturnValueSlot) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c48ae8)
#27 0x000055742563ccf3 clang::CodeGen::CodeGenFunction::EmitCall(clang::QualType, clang::CodeGen::CGCallee const&, clang::CallExpr const*, clang::CodeGen::ReturnValueSlot, llvm::Value*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c3ccf3)
#26 0x00005574255d92bf clang::CodeGen::CodeGenFunction::EmitCallArgs(clang::CodeGen::CallArgList&, clang::CodeGen::CodeGenFunction::PrototypeWrapper, llvm::iterator_range<clang::Stmt::CastIterator<clang::Expr, clang::Expr const* const, clang::Stmt const* const> >, clang::CodeGen::CodeGenFunction::AbstractCallee, unsigned int, clang::CodeGen::CodeGenFunction::EvaluationOrder) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3bd92bf)
#25 0x00005574255d7c5b clang::CodeGen::CodeGenFunction::EmitCallArg(clang::CodeGen::CallArgList&, clang::Expr const*, clang::QualType) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3bd7c5b)
#24 0x000055742562f8c9 clang::CodeGen::CodeGenFunction::EmitAnyExprToTemp(clang::Expr const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c2f8c9)
#23 0x000055742562f1fe clang::CodeGen::CodeGenFunction::EmitAnyExpr(clang::Expr const*, clang::CodeGen::AggValueSlot, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c2f1fe)
#22 0x0000557425650bc1 clang::CodeGen::CodeGenFunction::EmitAggExpr(clang::Expr const*, clang::CodeGen::AggValueSlot) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c50bc1)
#21 0x000055742564dc3b clang::StmtVisitorBase<std::add_pointer, (anonymous namespace)::AggExprEmitter, void>::Visit(clang::Stmt*) CGExprAgg.cpp:0:0
#20 0x000055742564d873 (anonymous namespace)::AggExprEmitter::VisitCallExpr(clang::CallExpr const*) CGExprAgg.cpp:0:0
#19 0x000055742564d49b (anonymous namespace)::AggExprEmitter::withReturnValueSlot(clang::Expr const*, llvm::function_ref<clang::CodeGen::RValue (clang::CodeGen::ReturnValueSlot)>) CGExprAgg.cpp:0:0
#18 0x0000557425649fcb clang::CodeGen::RValue llvm::function_ref<clang::CodeGen::RValue (clang::CodeGen::ReturnValueSlot)>::callback_fn<(anonymous namespace)::AggExprEmitter::VisitCallExpr(clang::CallExpr const*)::'lambda'(clang::CodeGen::ReturnValueSlot)>(long, clang::CodeGen::ReturnValueSlot) CGExprAgg.cpp:0:0
#17 0x0000557425648ae8 clang::CodeGen::CodeGenFunction::EmitCallExpr(clang::CallExpr const*, clang::CodeGen::ReturnValueSlot) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c48ae8)
#16 0x000055742563ccf3 clang::CodeGen::CodeGenFunction::EmitCall(clang::QualType, clang::CodeGen::CGCallee const&, clang::CallExpr const*, clang::CodeGen::ReturnValueSlot, llvm::Value*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c3ccf3)
#15 0x00005574255d92bf clang::CodeGen::CodeGenFunction::EmitCallArgs(clang::CodeGen::CallArgList&, clang::CodeGen::CodeGenFunction::PrototypeWrapper, llvm::iterator_range<clang::Stmt::CastIterator<clang::Expr, clang::Expr const* const, clang::Stmt const* const> >, clang::CodeGen::CodeGenFunction::AbstractCallee, unsigned int, clang::CodeGen::CodeGenFunction::EvaluationOrder) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3bd92bf)
#14 0x00005574255d7b74 clang::CodeGen::CodeGenFunction::EmitCallArg(clang::CodeGen::CallArgList&, clang::Expr const*, clang::QualType) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3bd7b74)
#13 0x000055742564731d clang::CodeGen::CodeGenFunction::EmitReferenceBindingToExpr(clang::Expr const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c4731d)
#12 0x000055742563ea5e clang::CodeGen::CodeGenFunction::EmitLValue(clang::Expr const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c3ea5e)
#11 0x00005574256476e6 clang::CodeGen::CodeGenFunction::EmitMaterializeTemporaryExpr(clang::MaterializeTemporaryExpr const*) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c476e6)
#10 0x0000557425643111 clang::CodeGen::CodeGenFunction::EmitAnyExprToMem(clang::Expr const*, clang::CodeGen::Address, clang::Qualifiers, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c43111)
 #9 0x0000557425650bc1 clang::CodeGen::CodeGenFunction::EmitAggExpr(clang::Expr const*, clang::CodeGen::AggValueSlot) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3c50bc1)
 #8 0x000055742564ed57 clang::StmtVisitorBase<std::add_pointer, (anonymous namespace)::AggExprEmitter, void>::Visit(clang::Stmt*) CGExprAgg.cpp:0:0
 #7 0x00005574256509f1 (anonymous namespace)::AggExprEmitter::VisitConstantExpr(clang::ConstantExpr*) CGExprAgg.cpp:0:0
 #6 0x00005574255caf8d clang::CodeGen::CodeGenFunction::EmitAggregateStore(llvm::Value*, clang::CodeGen::Address, bool) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3bcaf8d)
 #5 0x000055742549b577 clang::CodeGen::CGBuilderTy::CreateStructGEP(clang::CodeGen::Address, unsigned int, llvm::Twine const&) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x3a9b577)
 #4 0x000055742477ee02 llvm::PointerType::get(llvm::Type*, unsigned int) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x2d7ee02)
 #3 0x00007fc70ca033c0 __restore_rt (/lib/x86_64-linux-gnu/libpthread.so.0+0x153c0)
 #2 0x0000557424ef2ed8 CrashRecoverySignalHandler(int) CrashRecoveryContext.cpp:0:0
 #1 0x0000557424fb9150 llvm::sys::CleanupOnSignal(unsigned long) (/opt/compiler-explorer/clang-trunk/bin/clang+++0x35b9150)
 #0 0x0000557424fbb27f PrintStackTraceSignalHandler(void*) Signals.cpp:0:0

后端:中间代码优化器和llvm

源程序被转换为llvm ir之后的故事,llvm ir的生命历程已经有人写过了:https://eli.thegreenplace.net/2012/11/24/life-of-an-instruction-in-llvm

珠玉在前, 不写了 有空再写。

如何梳理clang的编译流程

clang的构建不用多说,要注意的是应该编译Debug版本的clang,因为我们要通过调试学习clang的内部架构. cmake 命令如下:

cmake -G Ninja -DCMAKE_BUILE_TYPE=Debug -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra"

要想搞懂一个如clang/llvm这样大型项目,来先配置好工具吧。

在命令行中用lldb或gdb调试clang

编译好的 clang 也就是个普通可执行程序,用lldb/gdb上去调试就行。如前所述, clang 是一个编译器驱动器,它运行时会fork自己,fork出的子进程再编排编译任务,所以打的断点进不去。解决这个问题有两种方法:

不使用 clang ,在lldb里直接调试 cc1

这么做需要我们告诉 cc1 该怎么编译,就是我们需要手动把 clang 喂给它的那些Option喂给它。难道要我们人脑梳理好它的Option吗?不用慌,我们可以问 clang:

cmake-build-debug/bin/clang -### /home/vitalyr/projects/learn/C++/algorithms_vitalyr/play/test_clang.cpp

输出为

clang version 15.0.0 (https://github.com/llvm/llvm-project.git 59d38f1b56d516f844733fe22294de7c78c8fbf6)
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/bin
 "/home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/bin/clang-15" "-cc1" "-triple" "x86_64-unknown-linux-gnu" "-emit-obj" "-mrelax-all" "--mrelax-relocations" "-disable-free" "-clear-ast-before-backend" "-main-file-name" "test_clang.cpp" "-mrelocation-model" "static" "-mframe-pointer=all" "-fmath-errno" "-ffp-contract=on" "-fno-rounding-math" "-mconstructor-aliases" "-funwind-tables=2" "-target-cpu" "x86-64" "-tune-cpu" "generic" "-mllvm" "-treat-scalable-fixed-error-as-warning" "-debugger-tuning=gdb" "-fcoverage-compilation-dir=/home/vitalyr/projects/contribute/cpp/llvm-project/llvm" "-resource-dir" "/home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/lib/clang/15.0.0" "-internal-isystem" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0" "-internal-isystem" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/x86_64-pc-linux-gnu" "-internal-isystem" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../include/c++/11.2.0/backward" "-internal-isystem" "/home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/lib/clang/15.0.0/include" "-internal-isystem" "/usr/local/include" "-internal-isystem" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../x86_64-pc-linux-gnu/include" "-internal-externc-isystem" "/include" "-internal-externc-isystem" "/usr/include" "-fdeprecated-macro" "-fdebug-compilation-dir=/home/vitalyr/projects/contribute/cpp/llvm-project/llvm" "-ferror-limit" "19" "-fgnuc-version=4.2.1" "-fcxx-exceptions" "-fexceptions" "-fcolor-diagnostics" "-faddrsig" "-D__GCC_HAVE_DWARF2_CFI_ASM=1" "-o" "/tmp/test_clang-78d0b0.o" "-x" "c++" "/home/vitalyr/projects/learn/C++/algorithms_vitalyr/play/test_clang.cpp"
 "/usr/bin/ld" "--eh-frame-hdr" "-m" "elf_x86_64" "-dynamic-linker" "/lib64/ld-linux-x86-64.so.2" "-o" "a.out" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../lib64/crt1.o" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../lib64/crti.o" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/crtbegin.o" "-L/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0" "-L/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../lib64" "-L/lib/../lib64" "-L/usr/lib/../lib64" "-L/home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/bin/../lib" "-L/lib" "-L/usr/lib" "/tmp/test_clang-78d0b0.o" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "-lc" "-lgcc" "--as-needed" "-lgcc_s" "--no-as-needed" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/crtend.o" "/usr/lib64/gcc/x86_64-pc-linux-gnu/11.2.0/../../../../lib64/crtn.o"

可以看到 clang 给我们生成了非常多的编译选项。然后,如此运行 =lldb:

lldb -- cmake-build-debug/bin/clang
(lldb) target create "cmake-build-debug/bin/clang"
Current executable set to '/home/vitalyr/projects/contribute/cpp/llvm-project/llvm/cmake-build-debug/bin/clang' (x86_64).
(lldb) b CXXNameMangler::mangle
Breakpoint 1: 2 locations.
(lldb) r -cc1 <Options> <file>

其中<Options>是前面 clang 要喂给 cc1 的那么长的选项,<file>是想要让 clang 编译的文件。

使用 -fintegrated-cc1

更简单的方法是给 clang 加上 -fintegrated-cc1 这个选项

使用CLion调试clang/llvm

我在命令行里对clang/llvm这种项目里找文件找函数打断点调试无压力的程度,在GUI里感觉更自在一些。好在clang/llvm是标准的用CMake组织起来的项目,即使是一个大型monorepo项目,但配置Clion来开发调试clang/llvm也很简单。

首先用CLion打开 llvm-project/llvm 文件夹,CLion会自动读取这个文件夹下的 CMakeLists.txt ,然后弹出对项目进行配置的而窗口,也可以通过 File | Settings | Build, Execution, Deploy | CMake 找到这个界面:

https://gitee.com/Vitaly/img/raw/master/images/Pictures/screenshots/2022-03-02-17-00-21-2022-03-02_16-54-d4c3.png 在CMake Options那里填上编译llvm的选项,以及需要包含进来的项目,例如我填的是:

-G "Ninja" -DLLVM_USE_LINKER=lld -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;compiler-rt;cross-project-tests;lld;lldb;mlir;polly"

这里使用llvm自己的 lld 链接器,开启了clang, clang-tools-extra等项目。 然后就可以像一个普通的C++项目调试了。注意clang driver的主函数在 clang/tools/driver/driver.cpp ,可以先在这里面打几个断点观察程序运行,然后进一步在它调用的你感兴趣的函数里打断点。

https://raw.githubusercontent.com/VitalyAnkh/img/main/images/Pictures/screenshots/2022-03-02-18-05-00-2022-03-02_17-20-ecf7.png

使用 llvm::errs() 给clang打log

修改clang的源代码,给函数所在的文件加上头文件,在感兴趣的函数里打log。例如,在 clang/lib/lex/Lex.cpp 里加上一行:

#include "llvm/Support/raw_ostream.h"

然后给想要考察的函数里加一行:

llvm::errs()<<"<the-function-name> log"<<'\n';

让它崩溃

使用llvm的测试框架