[toc]
|
|
进行后续的分享前,我们先回忆一下编译器前端的功能
从 main.m
到 a.out
的完整的编译流程
|
|
前言
clang
编译器前端负责从源码生成中间码,它通常由 clang
模块驱动,并通常包含以下几个步骤:
本文会先对 clang
编译器前端的流程进行简单的介绍,并会在后面的系列文章依次分享下面的几个库:
- clangLex :负责词法分析和预处理,处理宏、令牌和 pragma 构造
- clangAST:负责提供了构建、操作和遍历 AST 相关的功能
- clangParse:负责从词法分析的结果进行处理
- clangSema:负责语义分析
- clangCodeGen:负责生成 LLVM IR 代码
clang
clang
模块只包含 5
个可编译文件,大部分的功能都是依赖其它模块提供
clang
库 的driver.cpp
是整个程序的入口。
clang
模块主要负责以下任务:
-
main
函数检测输入的参数是否包含以-cc1
开头的参数 -
通过
ExecuteCC1Tool
函数分发不同的cc1
类型。clang
目前支持 3 种类型-
-cc1
: LLVM ‘Clang’ Compiler -
-cc1as
: Clang Integrated Assembler -
-cc1gen-reproducer
: Generate Libclang invocation reproducers
-
-
cc1_main
函数负责初始化CompilerInstance
、DiagnosticIDs
,并调用CreateFromArgs
函数构建CompilerInvocation
-
CreateFromArgs
函数内部会非常多的函数对参数进行解析 -
当
-emit-obj
参数传入时,ParseFrontendArgs
函数会将frontend::EmitObj
赋值给ProgramAction
后面的
CreateFrontendBaseAction
函数会依赖依赖ProgramAction
对
-emit-objc
不熟悉的朋友,可以看看clang driver
系列文章 -
调用
clangFrontendTool
模块的ExecuteCompilerInvocation
函数执行编译任务
clangFrontendTool
clangFrontendTool
非常简单,目前只包含一个可编译文件 ExecuteCompilerInvocation.cpp
-
ExecuteCompilerInvocation
会判断先是否存在-h
-v
等参数,如果存在,会执行对应的任务值得注意的是,这里会尝试加载 plugin
-
经过一系列的判断后,才会通过
CreateFrontendAction
创建需要执行的编译器前端任务 -
CreateFrontendAction
会先调用CreateFrontendBaseAction
创建FrontendAction
的子类 -
CreateFrontendBaseAction
函数会因为前文提到的EmitObj
参数创建EmitObjAction
的实例 -
任务创建完成后,会调用 clangFrontend 模块的
ExecuteAction
函数执行编译任务
clangFrontend
clangFrontend
模块包含了很多重要的功能。
cc1_main
函数创建的 CompilerInstance
就是来自 clangFrontend
CompilerInstance
是 clangFrontend
中非常重要的一个类。它持有了诸如 preprocessor
、Target
、AST
等属性
CompilerInstance 执行过程
CompilerInstance::ExecuteAction
会通过 Inputs
获取输入文件,并依次调用以下方法:
-
Act.BeginSourceFile()
-
Act.Execute()
-
Act.EndSourceFile()
Ac
就是之前提到的FrontendAction
的子类EmitObjAction
Act.BeginSourceFile()
-
Act.BeginSourceFile()
函数会通过懒加载方式创建FileManager
和SourceManager
FileManager
负责和文件系统交互,文件缓存、目录查找等任务SourceManager
负责查找并将文件缓存到内存 -
通过
createPreprocessor
函数创建预处理器预处理器 初始化时,会构建一个包含各个语言的关键字列表
IdentifierTable Identifiers;
,方便后续词法分析使用 -
通过
createASTContext
创建ASTContext
-
随后,会调用函数
FrontendAction::CreateWrappedASTConsumer
创建ASTConsumer
Act.Execute()
Act.Execute()
实际上是执行 EmitObjAction
继承自 FrontendAction
的 Execute
函数
FrontendAction:Execute
随后会调用 CodeGenAction::ExecuteAction
函数
CodeGenAction::ExecuteAction
函数会调用ASTFrontendAction::ExecuteAction
ASTFrontendAction::ExecuteAction
会调用 CompilerInstance
的 createSema
函数创建 Sema
Sema
后续会用于语义分析
各项准备工作结束后,就会调用 clangParse
模块的 clang::ParseAST
构建 abstract syntax tree
总结
本文对 clang 编译器前端入门知识进行了简单介绍。
下一篇文章,我们会开始分享 ParseAst
的第一个主要流程:词法分析和 pragma 处理