首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >clang 源码导读(4): clang driver 构建 Actions

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

作者头像
酷酷的哀殿
发布于 2021-03-18 09:49:21
发布于 2021-03-18 09:49:21
2.9K1
举报
文章被收录于专栏:酷酷的哀殿酷酷的哀殿

前言

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

image

当需要处理的源码文件列表 构建完成后,我们就可以根据参数和源文件类型计算需要的 Action 了。

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

  1. Action[1] 是执行的编译步骤基类,持有Input、Action 类型,产物类型等信息;可以理解为将某种输入转为输出文件的操作步骤,比如,PreprocessJobAction 可以将源码 main.m 转为 main.im
  2. InputAction[2] 是特例,只代表原始的输入文件/参数
  3. PreprocessJobAction[3] 是将源码进行预处理的过程
  4. CompileJobAction[4] 是将上一步的结果转为 bitcode 的过程
  5. BackendJobAction[5] 是将 bitcode 转为 .s 文件的过程
  6. AssembleJobAction[6] 是将.s 文件转为 .o 二进制文件的过程
  7. LinkJobAction[7] 是将 .o 文件合并为静态库/动态库/可执行文件的过程
  8. BindArchAction[8] 是特例,将 .o 文件与特定的架构做绑定
  9. LipoJobAction[9] 是用于将多个 BindArchAction 输入合并为单一的 fat mach-o 文件
  10. JobAction[10] 可以理解能够通过单独的程序执行的过程,注意:Input 和 BindArchAction 没有对应任何的过程

构建 Actions 的目的是什么?

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

  1. clang driver 需要根据 参数 计算需要进行的步骤 比如,当 -emit-llvm 参数传入时,编译器只需要 预处理、编译器前端 两步,不再需要进行 编译器后端 和 汇编 -emit-llvm 的含义是将输入文件编译为 bitcode 文件
  2. clang driver 需要根据 输入文件类型 计算需要进行的步骤 比如,当输入的源码文件是汇编类(扩展名是 .s )型时,只需要最后 汇编 阶段

BuildUniversalActions 方法源码分析

BuildUniversalActions 方法负责构建 Actions

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

image

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

image

  1. 调用 BuildActions 计算每个输入文件对应的 SingleActions (可能包含预处理、编译、后端、汇编等) 注意:BuildUniversalActions 的 SingleActions 参数传到 BuildActions 方法后,名字会变为 Actions

image

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

image

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

image

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

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

image

  1. 随后,types::getCompilationPhases 会通过函数 llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> types::getCompilationPhases(ID Id, phases::ID LastPhase) 根据文件类型 TY_ObjC 和 LastPhase 获取后续的 phase 列表 两个函数名相同,参数不一样

imageTypes.def 文件维护了不同文件类型默认情况下需要经历的 phase .m 需要经历以下 5 个阶段

image

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

image

  1. 随后,会依次遍历 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

image

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

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

  1. 当所有源码文件循环完毕后,会判断 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 的流程。

参考资料

[1]

Action: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1Action.html#details

[2]

InputAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1InputAction.html#details

[3]

PreprocessJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1PreprocessJobAction.html#details

[4]

CompileJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1CompileJobAction.html#details

[5]

BackendJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1BackendJobAction.html#details

[6]

AssembleJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1AssembleJobAction.html#details

[7]

LinkJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1LinkJobAction.html#details

[8]

BindArchAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1BindArchAction.html#details

[9]

LipoJobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1LipoJobAction.html#details

[10]

JobAction: https://clang.llvm.org/doxygen/classclang_1_1driver_1_1JobAction.html#details

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-11,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 前言
  • 构建 Actions 的目的是什么?
  • BuildUniversalActions 方法源码分析
  • Action 创建流程介绍
    • Preprocess
    • Compile
    • Backend
    • Assemble
    • Link
    • bind & Lipo
  • 总结
    • 参考资料
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档