| 从Clang到Pass加载与执行的流 一、gdb动调clang
 文章开始之前,先通过gd动调clang来看一看整个过程是什么样的。 开始调试 
 
从Clang到Pass加载与执行的流   我这里调试的是release版本的clang(不想再编译了),显然提示了找不到clang的调试符号。我建议使用debug版的clang来调试,不然就看不到断点处的源码了,这对调试还是有影响的(有些函数下不了断点,不知道是不是这个原因)。 直接把断点下在main函数处 重要的来了!clang在执行过程中,会fork子进程,如果直接进行跟踪会出现如下结果:
 
从Clang到Pass加载与执行的流   因此我们需要通过如下指令在gdb中设置跟踪子进程: | 1 
 | set follow-fork-mode child
 
 | 
 然后启动clang并运行我们的Pass,指令如下: | 1 
 | run -mllvm -mypass ~/Desktop/test.cpp -o ~/Desktop/test_debug_clang.ll
 
 | 
 
从Clang到Pass加载与执行的流   此时我们还需要对自己的Pass下断点: | 1 
 | b MyPass::runOnFunction
 
 | 
 继续运行直到命中我们下的Pass断点。 
从Clang到Pass加载与执行的流   成功断在了预期位置处,此时我们查看函数调用堆栈,指令为bt。 
从Clang到Pass加载与执行的流   从上面的函数调用堆栈可以明了的看出Pass从被加载到被执行的整个过程(实际上,它还是缺少了一些函数)。 接下来我们结合源码来仔细分析一下这个流程。二、从clang到Pass加载与执行的流程2.1 clang: 从源码到IR2.1.1 main clang的入口位于clang/tools/driver/driver.cpp中的main函数。 2.1.2 ExecuteCC1Tool| 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 
 | int main(int Argc, const char **Argv) {...
 // 从第二个参数开始搜索第一个非空参数(跳过了参数命令行中的clang)
 auto FirstArg = llvm::find_if(llvm::drop_begin(Args), [](const char *A) { return A != nullptr; });
 //如果FirstArg以"-cc1"开头
 if (FirstArg != Args.end() && StringRef(*FirstArg).startswith("-cc1")) {
 // If -cc1 came from a response file, remove the EOL sentinels.
 if (MarkEOLs) {
 auto newEnd = std::remove(Args.begin(), Args.end(), nullptr);
 Args.resize(newEnd - Args.begin());
 }
 return ExecuteCC1Tool(Args);//调用ExecuteCC1Tool函数进一步处理
 }
 // 在Driver::BuildCompilation()中真正的命令行解析之前,处理需要处理的选项
 ...
 //初始化driver
 Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
 ...
 //如果不需要在新的进程中调用cc1工具
 if (!UseNewCC1Process) {
 TheDriver.CC1Main = &ExecuteCC1Tool;//CC1Main指向ExecuteCC1Tool函数
 llvm::CrashRecoveryContext::Enable();
 }
 //调用BuildCompilation构建编译任务,里面会将-cc1加入到Args中
 std::unique_ptr C(TheDriver.BuildCompilation(Args));
 int Res = 1;
 bool IsCrash = false;
 if (C && !C->containsError()) {
 SmallVector<std::pair, 4> FailingCommands;
 //执行编译任务,里面会创建多进程,回到main函数开始的地方,执行ExecuteCC1Tool函数
 Res = TheDriver.ExecuteCompilation(*C, FailingCommands);
 // Force a crash to test the diagnostics.
 ...
 //处理执行编译命令时的失败情况
 ...
 }
 ...
 }
 
 
 | 
 ExecuteCC1Tool的具体实现在clang/tools/driver/driver.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | static int ExecuteCC1Tool(SmallVectorImpl &ArgV) {...
 StringRef Tool = ArgV[1];
 void *GetExecutablePathVP = (void *)(intptr_t)GetExecutablePath;
 if (Tool == "-cc1")
 return cc1_main(makeArrayRef(ArgV).slice(1), ArgV[0], GetExecutablePathVP);
 if (Tool == "-cc1as")
 return cc1as_main(makeArrayRef(ArgV).slice(2), ArgV[0],
 GetExecutablePathVP);
 if (Tool == "-cc1gen-reproducer")
 return cc1gen_reproducer_main(makeArrayRef(ArgV).slice(2), ArgV[0],
 GetExecutablePathVP);
 ...
 }
 
 
 | 
 在上一小节中,我们知道了第一个参数是-cc1,因此这里会调用cc1_main函数。2.1.3 cc1_main cc1_main的具体实现在clang/tools/driver/cc1_main.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | int cc1_main(ArrayRef Argv, const char *Argv0, void *MainAddr) {...
 //创建Clang编译器实例
 std::unique_ptr Clang(new CompilerInstance());
 IntrusiveRefCntPtr DiagID(new DiagnosticIDs());
 //注册了支持对象文件封装的 Clang 模块
 ...
 // 一系列初始化
 ...
 // 执行clang的前端
 {
 llvm::TimeTraceScope TimeScope("ExecuteCompiler");
 //调用ExecuteCompilerInvocation函数
 Success = ExecuteCompilerInvocation(Clang.get());
 }
 //后续的清理工作
 ...
 }
 
 
 | 
 这里主要是创建clang实例,调用ExecuteCompilerInvocation函数开始编译目标源代码。2.1.4 clang::ExecuteCompilerInvocation ExecuteCompilerInvocation函数的声明在clang/include/clang/FrontendTool/ExecuteCompilerInvocation.h中,具体实现在clang/lib/FrontendTool/ExecuteCompilerInvocation.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | bool ExecuteCompilerInvocation(CompilerInstance *Clang) {// 命令行参数中是否存在-help、-v的参数,是则返回对应的信息
 ...
 // 加载必要插件
 Clang->LoadRequestedPlugins();
 
 // 同样还是检查一些参数,例如-mllvm
 ...
 // 调用CreateFrontendAction函数创建前端操作对象
 std::unique_ptr Act(CreateFrontendAction(*Clang));
 if (!Act)
 return false;
 // 调用ExecuteAction函数,执行前端操作,也就是编译
 bool Success = Clang->ExecuteAction(*Act);
 ...
 return Success;
 }
 
 
 | 
 这里主要是创建FrontendAction对象并执行ExecuteAction函数。2.1.5 clang::CompilerInstance::ExecuteAction CompilerInstance类的声明在clang/include/clang/Frontend/CompilerInstance.h中,具体实现在clang/lib/Frontend/CompilerInstance.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 
 | bool CompilerInstance::ExecuteAction(FrontendAction &Act) {//准备工作和选项处理
 ...
 for (const FrontendInputFile &FIF : getFrontendOpts().Inputs) {
 if (hasSourceManager() && !Act.isModelParsingAction())
 getSourceManager().clearIDTables();
 //BeginSourceFile开始处理源文件
 if (Act.BeginSourceFile(*this, FIF)) {
 //调用Execute函数进行处理
 if (llvm::Error Err = Act.Execute()) {
 consumeError(std::move(Err));
 }
 //结束
 Act.EndSourceFile();
 }
 }
 //错误处理
 ...
 //生成代码输出
 ...
 return !getDiagnostics().getClient()->getNumErrors();
 }
 
 
 | 
 这里通过BeginSourceFile函数加载源文件到内存中了,然后调用了FrontendAction类的Execute函数进行编译。2.1.6 clang::FrontendAction::Execute FrontendAction类的声明在clang/include/clang/Frontend/FrontendAction.h中,具体实现在clang/lib/Frontend/FrontendAction.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 
 | llvm::Error FrontendAction::Execute() {//获取编译器实例的引用
 CompilerInstance &CI = getCompilerInstance();
 
 if (CI.hasFrontendTimer()) {
 ...
 ExecuteAction();//
 }
 else ExecuteAction();
 
 // If we are supposed to rebuild the global module index, do so now unless
 // there were any module-build failures.
 ...
 
 return llvm::Error::success();
 }
 
 
 | 
 该函数进一步调用ASTFrontendAction类的ExecuteAction函数。2.1.7 clang::ASTFrontendAction::ExecuteAction 这个函数在开头的函数调用堆栈图中并没有出现,然而实际上确实调用了(真不明白这个是什么原因),如下图所示: 
从Clang到Pass加载与执行的流   那么就来看一下这个函数的源码。ASTFrontendAction类的声明在clang/include/clang/Frontend/FrontendAction.h中,具体实现在clang/lib/Frontend/FrontendAction.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 
 | void ASTFrontendAction::ExecuteAction() {...
 //没有语义分析器则创建
 if (!CI.hasSema())
 CI.createSema(getTranslationUnitKind(), CompletionConsumer);
 //调用ParseAST分析AST语法树
 ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats, CI.getFrontendOpts().SkipFunctionBodies);
 }
 
 
 | 
 主要是创建语义分析器,调用 ParseAST 方法,开始解析抽象语法树(AST)。2.1.8 clang:  arseAST ParseAST函数的声明在clang/include/clang/Parse/ParseAST.h中,具体实现在clang/lib/Parse/ParseAST.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 
 | void clang:  arseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) { 
    ... 
    //获取AST消费者 
    ASTConsumer *Consumer = &S.getASTConsumer(); 
    //创建解析器 
    std::unique_ptr ParseOP(new Parser(S.getPreprocessor(), S, SkipFunctionBodies)); 
    Parser &   = *ParseOP.get(); 
    ... 
    //设置主源文件,开始处理主源文件中的内容 
    S.getPreprocessor().EnterMainSourceFile(); 
    //获取外部AST源,外部AST源通常用于提供额外的语义信息或进行增量编译 
    ExternalASTSource *External = S.getASTContext().getExternalSource(); 
    if (External) 
        //通知外部源开始翻译单元的处理 
        External->StartTranslationUnit(Consumer); 
    //获取词法分析器 
    bool HaveLexer = S.getPreprocessor().getCurrentLexer(); 
    if (HaveLexer) { 
        llvm::TimeTraceScope TimeScope("Frontend"); 
        P.Initialize(); 
        Parser:  eclGroupPtrTy ADecl;//用于存储解析器解析的顶层声明组 
        Sema::ModuleImportState ImportState;//模块导入的状态 
        //PotentiallyEvaluated用于在语义分析期间设置表达式求值的上下文 
        EnterExpressionEvaluationContext PotentiallyEvaluated(S, Sema::ExpressionEvaluationContext:  otentiallyEvaluated); 
        //解析源文件中的顶层声明,并将它们传递给 AST 消费者进行处理 
        for (bool AtEOF = P.ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; AtEOF = P.ParseTopLevelDecl(ADecl, ImportState)) { 
            if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) 
                return; 
        } 
    }
 
    // 处理由#pragma weak生成的顶层声明 
    for (Decl *D : S.WeakTopLevelDecls()) 
        Consumer->HandleTopLevelDecl(DeclGroupRef(D)); 
    Consumer->HandleTranslationUnit(S.getASTContext());
 
    //收尾工作 
    ... 
}
 | 
 这部分真正是对源码进行语法树构建,并通过HandleTranslationUnit函数交给AST Consumer处理。2.1.9 clang::BackendConsumer::HandleTranslationUnit BackendConsumer类的声明在clang/include/clang/CodeGen/CodeGenAction.h中,具体实现在clang/lib/CodeGen/CodeGenAction.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | void BackendConsumer::HandleTranslationUnit(ASTContext &C) {{
 ...
 //调用了一个叫做 HandleTranslationUnit 的函数来处理翻译单元
 Gen->HandleTranslationUnit(C);
 ...
 }
 ...
 //设置 LLVM 的诊断处理程序和配置优化记录文件
 ...
 // 链接LinkModule到我们得模块中
 if (LinkInModules(getModule()))
 return;
 //遍历模块中的函数
 for (auto &F : getModule()->functions()) {
 //通过函数名获取对应的声明
 if (const Decl *FD = Gen->GetDeclForMangledName(F.getName())) {
 //将函数名的哈希值和声明的位置信息存储在ManglingFullSourceLocs中
 auto Loc = FD->getASTContext().getFullLoc(FD->getLocation());
 auto NameHash = llvm::hash_value(F.getName());
 ManglingFullSourceLocs.push_back(std::make_pair(NameHash, Loc));
 }
 }
 ...
 //EmbedBitcode用于处理-fembed-bitcode参数,目的是用于在生成的obj文件中增加一个用于存放bitcode的section
 EmbedBitcode(getModule(), CodeGenOpts, llvm::MemoryBufferRef());
 //clang后端代码生成(.ll或.bc文件)
 EmitBackendOutput(Diags, HeaderSearchOpts, CodeGenOpts, TargetOpts, LangOpts,
 C.getTargetInfo().getDataLayoutString(), getModule(),
 Action, FS, std::move(AsmOutStream), this);
 ...
 }
 
 
 | 
 该函数主要是记录了模块中函数的函数名哈希值和声明位置信息,最后调用EmitBackendOutput函数生成中间代码。 2.1.10 clang::EmitBackendOutput什么是模块? 在LLVM中,"模块(Module)"通常是指一个编译单元或一个源代码文件被编译后生成的中间表示(IR,Intermediate Representation)的集合。在 LLVM 中,每个模块都是一个独立的单元,包含了函数、全局变量、类型定义等信息,可以被独立地优化和编译。
 EmitBackendOutput函数声明在clang/include/clang/CodeGen/BackendUtil.h,具体实现在clang/lib/CodeGen/BackendUtil.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 
 | void clang::EmitBackendOutput(DiagnosticsEngine &Diags,const HeaderSearchOptions &HeaderOpts,
 const CodeGenOptions &CGOpts,
 const clang::TargetOptions &TOpts,
 const LangOptions &LOpts,
 StringRef TDesc, Module *M,
 BackendAction Action,
 std::unique_ptr OS) {
 ...
 //检查是否启用了ThinLTO(链接时优化技术),并进行相应操作
 ...
 //创建了EmitAssemblyHelper实例,用于辅助执行汇编输出的相关操作
 EmitAssemblyHelper AsmHelper(Diags, HeaderOpts, CGOpts, TOpts, LOpts, M);
 //根据Action执行相应的汇编操作
 if (CGOpts.LegacyPassManager)//旧版本使用LegacyPassManager
 AsmHelper.EmitAssemblyWithLegacyPassManager(Action, std::move(OS));
 else//新版本
 AsmHelper.EmitAssembly(Action, std::move(OS));
 // 验证生成的目标代码是否与目标描述一致
 if (AsmHelper.TM) {
 std::string DLDesc = M->getDataLayout().getStringRepresentation();
 ...
 }
 }
 
 
 | 
 最终通过BackendConsumer将AST转换成了IR代码,之后CGOpts.LegacyPassManager标志选择执行新版本的EmitAssemblyWithLegacyPassManager或是旧版本的EmitAssembly。 2.2 Pass加载Clang 的后端消费者(BackendConsumer)是 Clang 的一部分,它负责将 Clang 前端产生的抽象语法树(AST)转换为 LLVM 的中间表示(IR)
 很奇怪,这一部分的EmitAssemblyHelper类的函数无法触发断点,且提示没有加载进来该函数。2.2.1 EmitAssemblyHelper::EmitAssemblyWithLegacyPassManager 该函数同样也在clang/lib/CodeGen/BackendUtil.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
 | void EmitAssemblyHelper::EmitAssemblyWithLegacyPassManager( 
    ... 
    DebugifyCustomPassManager PerModulePasses;//创建模块Pass管理器 
    ... 
    PerModulePasses.add(createTargetTransformInfoWrapperPass(getTargetIRAnalysis()));//将目标IR分析包装成Pass并加入到模块Pass管理器中 
    legacy::FunctionPassManager PerFunctionPasses(TheModule);//创建函数Pass管理器 
    PerFunctionPasses.add(createTargetTransformInfoWrapperPass(getTargetIRAnalysis())); 
    //创建Pass,并添加到对应的Pass管理器的执行队列中 
    CreatePasses(PerModulePasses, PerFunctionPasses); 
    ... 
    legacy:  assManager CodeGenPasses; 
    CodeGenPasses.add(createTargetTransformInfoWrapperPass(getTargetIRAnalysis())); 
    //根据Action执行相应操作 
    ... 
    {... 
        //对模块中已声明的函数进行Pass优化 
        PerFunctionPasses.doInitialization(); 
        for (Function &F : *TheModule) 
            if (!F.isDeclaration()) 
                PerFunctionPasses.run(F); 
        PerFunctionPasses.doFinalization(); 
    } 
    {... 
        PerModulePasses.run(*TheModule);//对每个模块进行Pass优化 
    } 
    {... 
        CodeGenPasses.run(*TheModule); 
    } 
    //保留输出文件 
    ... 
}
 | 
 这部分代码主要是创建出两个重要的Pass管理器:PerModulePasses、PerFunctionPasses。然后调用CreatePasses函数创建Pass并添加到对应的Pass管理器的执行队列中(详见2.2.2小节)。之后就是调用PerFunctionPasses.run(F)、PerModulePasses.run(*TheModule)、CodeGenPasses.run(*TheModule)来执行Pass(详见2.3小节,以PerModulePasses.run函数为例进行讲解)。2.2.2 EmitAssemblyHelper::CreatePasses CreatePasses函数同样也在clang/lib/CodeGen/BackendUtil.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | void EmitAssemblyHelper::CreatePasses(legacy:  assManager &MPM, legacy::FunctionPassManager &FPM) { 
    ... 
    //创建PassManagerBuilderWrapper,用于配置和设置 Pass 
    PassManagerBuilderWrapper PMBuilder(TargetTriple, CodeGenOpts, LangOpts); 
    // 根据优化等级执行操作 
    ... 
    //设置PMBuilder的一些参数 
    ... 
    // 添加针对特定需求的扩展Pass, 通过Extensions向量来存储,此时并没有进行执行队列中 
    ... 
    // Set up the per-function pass manager. 
    ... 
    // Set up the per-module pass manager. 
    ... 
    //将配置好的Pass添加到函数级别和模块级别的Pass管理器中,进入执行队列中 
    PMBuilder.populateFunctionPassManager(FPM); 
    PMBuilder.populateModulePassManager(MPM); 
}
 | 
 最后调用的populateModulePassManager、populateFunctionPassManager函数将Pass添加到对应管理器的执行队列中(详见2.2.3小节,以populateModulePassManager函数为例进行讲解)。2.2.3 PassManagerBuilder::populateModulePassManager populateModulePassManager函数在llvm/lib/Transforms/IPO/PassManagerBuilder.cpp中。这个函数想必大家都很熟悉,因为在之前的OLLVM移植、编写自己的Pass都需要在这里面进行添加。 | 1 2
 3
 4
 5
 
 | void PassManagerBuilder::populateModulePassManager(legacy:  assManagerBase &MPM) { 
    //一系列Pass的添加 
    MPM.add(createAnnotation2MetadataLegacyPass()); 
    ... 
}
 | 
 到这里,可以认为我们的Pass已经创建好了,不再进行进一步深究。2.3 Pass执行2.3.1 PassManager::run PerModulePasses.run在llvm/lib/IR/LegacyPassManager.cpp中。 | 1 2
 3
 
 | bool PassManager::run(Module &M) {return PM->run(M);
 }
 
 
 | 
 最终调用PassManagerImpl::run函数2.3.2 llvm::legacy:  assManagerImpl PassManagerImpl类的定义在llvm/include/llvm/IR/LegacyPassManager.h中,具体实现在llvm/lib/IR/LegacyPassManager.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | bool PassManagerImpl::run(Module &M) {...
 //对所有的不可变 Pass 进行初始化
 for (ImmutablePass *ImPass : getImmutablePasses())
 Changed |= ImPass->doInitialization(M);//执行 Pass 的初始化工作
 initializeAllAnalysisInfo();//初始化所有的分析信息
 //遍历了所有包含在 Pass 管理器中的子管理器(Manager)
 for (unsigned Index = 0; Index < getNumContainedManagers(); ++Index) {
 //对模块M执行Pass
 Changed |= getContainedManager(Index)->runOnModule(M);
 M.getContext().yield();
 }
 //对所有的不可变 Pass 进行收尾工作
 for (ImmutablePass *ImPass : getImmutablePasses())
 Changed |= ImPass->doFinalization(M);
 
 return Changed;
 }
 
 
 | 
 该函数主要是对模块执行所有被安排执行的Pass,对应代码中第15~19行,关键函数为runOnModule。 2.3.3 FPPassManager::runOnModule什么是不可变Pass?在 LLVM 中,Pass(通常称为优化 Pass 或者分析 Pass)是指一种对 LLVM IR 进行转换或者分析的模块。Passes 可以用于执行各种任务,例如优化代码、收集统计信息、生成调试信息等。Passes 通常根据其行为被分为两类:可变 Pass 和不可变 Pass。
 可变 Pass(Mutable Pass):可变 Pass 是指在执行过程中可以修改 LLVM IR 的 Pass。这意味着它们可以插入、删除或修改指令、函数、基本块等内容。可变 Pass 通常用于优化编译过程,例如执行指令调度、函数内联等。不可变 Pass(Immutable Pass):不可变 Pass 是指在执行过程中不会修改 LLVM IR 的 Pass。它们只会读取 IR 并执行一些分析或者只读的转换。不可变 Pass 通常用于收集信息、生成报告、进行静态分析等任务。
 FPPassManager类的定义在llvm/include/llvm/IR/LegacyPassManagers.h中,具体实现在llvm/lib/IR/LegacyPassManager.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 
 | bool FPPassManager::runOnModule(Module &M) {bool Changed = false;
 
 for (Function &F : M)
 Changed |= runOnFunction(F);
 
 return Changed;
 }
 
 
 | 
 调用runOnFunction函数对模块中的函数进行Pass操作。2.3.4 FPPassManager::runOnFunction runOnFunction函数具体实现在llvm/lib/IR/LegacyPassManager.cpp中。 | 1 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 
 | bool FPPassManager::runOnFunction(Function &F) {//首先检查函数是否是一个声明(declaration),如果是的话,说明这个函数只是声明但没有定义,那么就没有必要对其进行优化
 if (F.isDeclaration()) return false;
 ...
 Module &M = *F.getParent();//获取函数所在模块
 //从模块级别的pass manager中获取继承的分析(analysis)信息
 populateInheritedAnalysis(TPM->activeStack);
 
 // Collect the initial size of the module.
 ...
 
 // Store name outside of loop to avoid redundant calls.
 const StringRef Name = F.getName();
 llvm::TimeTraceScope FunctionScope("OptFunction", Name);
 //遍历需要执行的pass列表
 for (unsigned Index = 0; Index < getNumContainedPasses(); ++Index) {
 FunctionPass *FP = getContainedPass(Index);//获取当前pass
 bool LocalChanged = false;
 ...
 //初始化分析 Pass
 initializeAnalysisImpl(FP);
 {
 PassManagerPrettyStackEntry X(FP, F);
 TimeRegion PassTimer(getPassTimer(FP));
 #ifdef EXPENSIVE_CHECKS
 uint64_t RefHash = FP->structuralHash(F);
 #endif
 LocalChanged |= FP->runOnFunction(F);//执行Pass
 ...
 }
 //清理工作
 ...
 }
 return Changed;
 }
 
 
 | 
 最后通过runOnFunction执行对应的Pass(代码中第28行),在当前例子中,也就是执行MyPass的runOnFunction函数。三、结语 以上就是。简而言之,首先clang会先将我们的目标源码转成AST语法树,然后再通过ASTConsumer换成IR代码。之后加载通过CreatePasses函数创建Pass并加入到执行队列中,后续会调用我们熟知的populateModulePassManager,这里注册过我们自定义的Pass。最后就是Pass执行,主要还是通过对应的Pass管理器的runOnModule函数来进行的,它这里面会直接调用我们自定义Pass的runOnModule函数。值得一提的是,Pass的加载与执行的操作都是在EmitAssemblyWithLegacyPassManager或EmitAssembly函数中调用和完成的。 
 参考: </std::pair
 |