酷酷的哀殿


  • 首页

  • 技术

  • 笔记

  • 杂记

  • Todo

  • 关于

  • 搜索
close

clang 源码导读(4): clang driver 构建 Actions

时间: 2021-03-02   |   阅读: 2443 字 ~5分钟

[toc]

前言

本篇文章会对 clang driver 的 构建 Actions 流程进行详细的讲解

image

正式分享前,我们先按照惯例分享本文涉及的主要 类图 和 流程图,方便对 构建 Action 的主要流程进行理解

  1. Compilation 负责维护一系列的 Actions 和 Jobs。本文的主要内容就是构建 Actions

  2. Action 是执行的编译步骤基类,持有Input、Action 类型,产物类型等信息;可以理解为将某种输入转为输出文件的操作步骤,比如,PreprocessJobAction 可以将源码 main.m 转为 main.im

  3. InputAction 是特例,只代表原始的输入文件/参数

  4. PreprocessJobAction 是将源码进行预处理的过程

  5. CompileJobAction 是将上一步的结果转为 bitcode 的过程

  6. BackendJobAction 是将 bitcode 转为 .s 文件的过程

  7. AssembleJobAction 是将.s 文件转为 .o 二进制文件的过程

  8. LinkJobAction 是将 .o 文件合并为静态库/动态库/可执行文件的过程

  9. BindArchAction 是特例,将 .o 文件与特定的架构做绑定

  10. LipoJobAction 是用于将多个 BindArchAction 输入合并为单一的 fat mach-o 文件

  11. JobAction 可以理解能够通过单独的程序执行的过程,注意:Input 和 BindArchAction 没有对应任何的过程

 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
40
41
42
43
44
45
46
47
48

classDiagram

  class Compilation {
    // 记录需要执行的 action
    ActionList Actions;
    /
    // 记录需要执行的 job
    JobList Jobs;

  }

  class Action {
    /
    // 类型,比如 CompileJobClass
    ActionClass Kind;

     /
    // 产物文件类型 TY_LLVM_BC
    types::ID Type;
    /
    // 依赖的 action,比如 CompileJobAction 依赖 PreprocessJobAction
    ActionList Inputs;
  }

  class  InputAction {
    // 参数
    const llvm::opt::Arg &Input;
  }

  class BindArchAction {
    // 架构
    StringRef ArchName;
  }


  Action <|-- JobAction
  Action <|-- InputAction

  JobAction <|-- PreprocessJobAction


  JobAction <|-- CompileJobAction
  JobAction <|-- AssembleJobAction
  JobAction <|-- BackendJobAction
  JobAction <|-- LinkJobAction
  JobAction <|-- DsymutilJobAction
  Action <|-- BindArchAction
 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
graph TD
  subgraph clangDriver
    Driver::BuildCompilation-->
    Driver::BuildUniversalActions--1.1 根据参数 options::OPT_arch 构建 Archs 数组-->Archs
    Driver::BuildUniversalActions--"1.2 参数 options::OPT_arch 不存在时;根据 triple 获取默认的架构"-->ToolChain::getDefaultUniversalArchName
    Driver::BuildUniversalActions--"1.3 构建 JobActions(支持 lipo 操作)"-->
    Driver::BuildActions--1.4 处理输入的参数-->
    Driver::handleArguments

    Driver::BuildActions--1.5 遍历每个输入文件-->for-Inputs
    for-Inputs--2.1 根据 参数和输入文件类型获取需要进行的 phase-->别名["types::getCompilationPhases(const clang::driver::Driver &Driver, llvm::opt::DerivedArgList &DAL, ID Id)"]-->getInfo
    for-Inputs--2.2 创建 InputAction-->别名1["创建 InputAction"]
    for-Inputs--2.3 遍历 phase-->for-Phase
    for-Phase-- 2.4.1 根据 phase 创建 action-->Driver::ConstructPhaseAction
        for-Phase-- 2.4.2 如果是 link phase--> 别名6["添加到 LinkerInputs"]



    Driver::ConstructPhaseAction--2.5.1 预处理 phase 先获取输出类型-->types::getPreprocessedType
    Driver::ConstructPhaseAction--2.5.2 预处理 phase 生成 PreprocessJobAction-->别名2["创建 PreprocessJobAction"]
    Driver::ConstructPhaseAction--2.7 编译 phase -->别名3["创建 CompileJobAction(Input, types::TY_LLVM_BC)"]
    Driver::ConstructPhaseAction--2.8 Backend phase -->别名4["创建 BackendJobAction(Input, types::TY_PP_Asm)"]
    Driver::ConstructPhaseAction--2.9 Assemble phase -->别名5["创建 AssembleJobAction(std::move(Input), types::TY_Object)"]
    Driver::BuildActions--"2.10 LinkerInputs 数量大于0时,需要创建 LinkJobAction"-->别名7["创建 LinkJobAction"]


    Driver::BuildUniversalActions--3.1 遍历 JobActions -->for-SingleActions--"3.2 根据 Archs 创建对应数量的 BindArchAction"-->别名8["创建 BindArchAction"]
    for-SingleActions--"3.3 如果 BindArchAction 数量大于0,创建 LipoJobAction"-->别名9["创建 LipoJobAction"]

end

  main --> Driver::BuildCompilation

构建 Actions 的目的是什么?

构建 Actions 的目的是为了满足以下目的:

  1. clang driver 需要根据 参数 计算需要进行的步骤

    比如,当 -emit-llvm 参数传入时,编译器只需要 预处理、编译器前端 两步,不再需要进行 编译器后端 和 汇编

    -emit-llvm 的含义是将输入文件编译为 bitcode 文件

  2. clang driver 需要根据 输入文件类型 计算需要进行的步骤

    比如,当输入的源码文件是汇编类(扩展名是 .s )型时,只需要最后 汇编 阶段

BuildUniversalActions 方法源码分析

BuildUniversalActions 方法负责构建 Actions

  1. 根据 -arch 参数生成需要处理的 Archs ,留待后续的处理使用

    image

  2. 如果没有传入 -arch 参数,可以通过 ToolChain::getDefaultUniversalArchName() 方法获取 triple 对应的架构

    image

  3. 调用 BuildActions 计算每个输入文件对应的 SingleActions (可能包含预处理、编译、后端、汇编等)

    注意: BuildUniversalActions 的 SingleActions 参数传到 BuildActions 方法后,名字会变为 Actions

    image

  4. BuildActions 会先调用 Driver::handleArguments 方法对参数进行一些处理

    image

  5. 随后,会遍历输入源码文件Inputs,并通过 llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> types::getCompilationPhases(const clang::driver::Driver &Driver,llvm::opt::DerivedArgList &DAL, ID Id) 获取需要对输入文件进行处理的 phase 数组

    image

  6. types::getCompilationPhases 内部会根据传入的参数获取需要执行的最后一个 phase

    image

    通过 -ccc-print-phases 参数可以对比两种场景的差异,比如,当 -emit-llvm 参数传入时,就会将移除 Backend 后面的 Assemble

    image

  7. 随后,types::getCompilationPhases 会通过函数 llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> types::getCompilationPhases(ID Id, phases::ID LastPhase) 根据文件类型 TY_ObjC 和 LastPhase 获取后续的 phase 列表

    两个函数名相同,参数不一样

    image

    Types.def 文件维护了不同文件类型默认情况下需要经历的 phase

    .m 需要经历以下 5 个阶段

    image

  8. 下一步,Driver::BuildActions 方法会先组装一个 InputAction (每个 Job 都包含一个 Kind 属性,代表该 Job 的类型,InputAction 的 Kind是 InputClass ),InputAction 就相当于输入文件的占位符

    image

  9. 随后,会依次遍历 phase,并根据 phase 创建 Action(通过调用 ConstructPhaseAction 函数实现)

    注意,每个 NewCurrent 都会持有 Current

    image

Action 创建流程介绍

本节会介绍通过除 InputAction 以外的 Action 创建流程

Preprocess

ConstructPhaseAction 方法检测到 phases::Preprocess 时会依次进行以下处理:

  1. 根据文件类型获取 TT_ObjC

  2. 根据 TT_ObjC 获取输出文件的类型 TY_PP_ObjC

  3. 通过 Input 和 OutputTy 构建 PreprocessJobAction

    image

    1. .m 文件支持的第一个 phase 是 phases::Preprocess
    2. .m 的预处理类型同样由 Types.def 文件维护

    image

Compile

phases::Compile 代表编译器的 前端 流程

phases::Compile 同样会根据传入的参数判断需要组装的类型,比如是否存在 -rewrite-objc 、-emit-ast 等参数

本例中,会构建 CompileJobAction(该 action 会生成 types::TY_LLVM_BC 文件)

image

Backend

phases::Backend 就是我们通常所说的 编译器后端

phases::Backend 负责组装 BackendJobAction,本例中,该 JobAction 的输出文件类型是 TY_PP_Asm (文件扩展名是 .s)

image

Assemble

phases::Assemble 会组装 AssembleJobAction ,该 JobAction 的输出文件类型为 TY_Object (文件扩展名是 .o)

image

Link

因为 link 是可以将一个或多个源码文件产出的 .o 文件进行链接,所以,LinkAction 会稍微复杂一些:

  1. Driver::BuildActions 方法会维护一个 LinkerInputs 数组,负责记录需要进行 link 操作的 JobAction

    当某个源码文件需要进行 link 操作时,就会先临时保存到 LinkerInputs 数组

    image

  2. 当所有源码文件循环完毕后,会判断 LinkerInputs 是否为空;如果非空,会增加一个 LinkJobAction 进行下一步处理

    image

    截止到这一步, 所有的 Action 就会构造为一个类似于链表的构造

    image

bind & Lipo

link action 创建完毕后,会根据 BuildUniversalActions 生成的 Archs 数组创建对应数量的 BindArchAction,该JobAction 记录需要产出文件的架构,比如 arm64 或者 armv7

如果 Arch 数量大于 1,会新增一个 LipoJobAction ,LipoJobAction 会将不同的架构的二进制合并为一个 fat mach-o 文件

image

经过这一步后,所有的 Action 就可以组成下面这种结构:

image

总结

本文通过对 BuildUniversalActions 方法的源码分析,介绍了 clang driver 构建 Actions 的流程。

相关推荐

  • clang 源码导读(1): clang 导读
  • clang 源码导读(2): clang driver 流程简介
  • clang 源码导读(3): clang driver 参数解析
  • clang 源码导读(5): clang driver 构建 Jobs
  • clang 源码导读(6): clang driver 执行命令
#clang# #clang driver#
clang 源码导读(3): clang driver 参数解析
clang 源码导读(5): clang driver 构建 Jobs
  • 文章目录
  • 站点概览
酷酷的哀殿

酷酷的哀殿

单身狗

74 日志
83 标签
    • 前言
    • 构建 Actions 的目的是什么?
    • BuildUniversalActions 方法源码分析
    • Action 创建流程介绍
      • Preprocess
      • Compile
      • Backend
      • Assemble
      • Link
      • bind & Lipo
    • 总结
© 2021 酷酷的哀殿 京ICP备18052541号-2
Powered by - Hugo v0.80.0
Theme by - NexT
0%