美文网首页
clang driver

clang driver

作者: 纯情_小火鸡 | 来源:发表于2021-09-29 14:22 被阅读0次

    clang不止是前端编译器,更是连接了LLVM整个编译过程和其他工具的一个驱动程序。

    clang/include/clang/Basic 目录下定义了众多td模版文件,例如DiagnosticDriverKinds.td就是Driver的相关诊断信息。对应的类型则是在 clang/include/clang/Basic/DiagnosticIDs.h 文件中定义了6个类型:

    /// Used for handling and querying diagnostic IDs.
    ///
    /// Can be used and shared by multiple Diagnostics for multiple translation units.
    class DiagnosticIDs : public RefCountedBase<DiagnosticIDs> {
    public:
      /// The level of the diagnostic, after it has been through mapping.
      enum Level {
        Ignored, Note, Remark, Warning, Error, Fatal
      };
    

    clang driver

    clang Driver 负责拼接编译器命令和 ld 命令。

    他的处理原理如下:

    1. Parse:解析传入的参数
    2. Pipeline:根据每个输入的文件和类型,组建action,具体类型可以查看ActionClass枚举类型,对应到具体的JobAction
    3. Bind:根据action选择对应的工具和文件名信息,具体可以通过 clang -ccc-print-bindings 进行查看
    4. Translate:将输入的参数转换为不同的tool的参数。如clang -cc1 -arch arm64,在clang中使用的是-triple arm64-apple-ios14,而ld则会使用-arch arm64
    5. Execute:调用tool执行任务。该步骤会通过创建子进程方式调用tool。

    举个实例:

    /Test » xcrun -l clang test.c -v -O2 -o Test                                                                             n14637@GIH-D-21687
    env SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang test.c -v -O2 -o Test
    Apple clang version 12.0.0 (clang-1200.0.32.29)
    Target: x86_64-apple-darwin19.6.0
    Thread model: posix
    InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
     "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" -cc1 -triple x86_64-apple-macosx10.15.0 -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -Werror=implicit-function-declaration -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name test.c -mrelocation-model pic -pic-level 2 -mthread-model posix -mframe-pointer=all -fno-strict-return -masm-verbose -munwind-tables -target-sdk-version=10.15.6 -fcompatibility-qualified-id-block-type-checking -target-cpu penryn -dwarf-column-info -debugger-tuning=lldb -target-linker-version 609.8 -v -resource-dir /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0 -isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -I/usr/local/include -internal-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/local/include -internal-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include -internal-externc-isystem /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include -internal-externc-isystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include -O2 -Wno-reorder-init-list -Wno-implicit-int-float-conversion -Wno-c99-designator -Wno-final-dtor-non-final-class -Wno-extra-semi-stmt -Wno-misleading-indentation -Wno-quoted-include-in-framework-header -Wno-implicit-fallthrough -Wno-enum-enum-conversion -Wno-enum-float-conversion -fdebug-compilation-dir /Test -ferror-limit 19 -fmessage-length 164 -stack-protector 1 -fstack-check -mdarwin-stkchk-strong-link -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fobjc-runtime=macosx-10.15.0 -fmax-type-align=16 -fdiagnostics-show-option -fcolor-diagnostics -vectorize-loops -vectorize-slp -o /var/folders/71/830m4bcj2_v1ly5qbtsbhxm4w50c2h/T/test-72900e.o -x c test.c
    clang -cc1 version 12.0.0 (clang-1200.0.32.29) default target x86_64-apple-darwin19.6.0
    ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/local/include"
    ignoring nonexistent directory "/Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/Library/Frameworks"
    #include "..." search starts here:
    #include <...> search starts here:
     /usr/local/include
     /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/include
     /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/usr/include
     /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include
     /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks (framework directory)
    End of search list.
     "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -arch x86_64 -platform_version macos 10.15.0 10.15.6 -syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk -o Test -L/usr/local/lib /var/folders/71/830m4bcj2_v1ly5qbtsbhxm4w50c2h/T/test-72900e.o -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/12.0.0/lib/darwin/libclang_rt.osx.a
    

    可以看到,执行xcrun之后,实际上clang构造了两个job,一个是编译任务,一个是链接任务,最后根据job创建两个进程执行任务。

    看到源码部分,在driver.cpp:

        //解析参数,该方法会调用DriverOptTable中的getDriverOptTable方法判断clang driver支持的所有参数类型
        //其内部的OptTable::Info InfoTable[]是通过clang/Driver/Options.inc生成的,而inc是由tablegen将optins.td转换的
      IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts =
          CreateAndPopulateDiagOpts(argv);
        //诊断引擎绑定到一个翻译单元和一个SourceManager。
        DiagnosticsEngine Diags(DiagID, &*DiagOpts, DiagClient);
        //并将diagnostics传递给DiagnosticConsumer,以便向用户报告
      if (!DiagOpts->DiagnosticSerializationFile.empty()) {
        auto SerializedConsumer =
            clang::serialized_diags::create(DiagOpts->DiagnosticSerializationFile,
                                            &*DiagOpts, /*MergeChildRecords=*/true);
        Diags.setClient(new ChainedDiagnosticConsumer(
            Diags.takeClient(), std::move(SerializedConsumer)));
      }
        ProcessWarningOptions(Diags, *DiagOpts, /*ReportDiags=*/false);
        //创建driver实例
        Driver TheDriver(Path, llvm::sys::getDefaultTargetTriple(), Diags);
      SetInstallDir(argv, TheDriver, CanonicalPrefixes);
      TheDriver.setTargetAndMode(TargetAndMode);
        //xxx
        //1.构造需要执行的命令,跳转到最下面的方法👇
      std::unique_ptr<Compilation> C(TheDriver.BuildCompilation(argv));
    
        //2.构建action
      if (TC.getTriple().isOSBinFormatMachO())
        BuildUniversalActions(*C, C->getDefaultToolChain(), Inputs);
      else
        BuildActions(*C, C->getArgs(), Inputs, C->getActions());
        //3.构建jobs
      BuildJobs(*C);
    
        //4.执行任务
      Driver::ExecuteCompilation -> Compilation::ExecuteJobs -> Compilation::ExecuteCommand-> Command::Execute -> llvm::sys::ExecuteAndWait
    
    
        
        Compilation *Driver::BuildCompilation(ArrayRef<const char *> ArgList) {
            //xxx
            //1.1.根据命令行指定的参数进行解析
          CLOptions = std::make_unique<InputArgList>(
          ParseArgStrings(ArgList.slice(1), IsCLMode(), ContainsError));
            //1.2.获取triple并通过gettoolchain获取对应的toolchain
          const ToolChain &TC = getToolChain(
          *UArgs, computeTargetTriple(*this, TargetTriple, *UArgs));
                //1.3.Compilation类接管参数
            Compilation *C = new Compilation(*this, TC, UArgs.release(), TranslatedArgs,
                                       ContainsError);
            //1.4.获取输入文件进行编译,该方法会通过输入的文件扩展后缀获取文件类型
          BuildInputs(C->getDefaultToolChain(), *TranslatedArgs, Inputs);
      }
    
        //1.1.1.
        InputArgList Driver::ParseArgStrings(ArrayRef<const char *> ArgStrings,
                                         bool IsClCompatMode,
                                         bool &ContainsError) {
            //调用getOpts获取支持的所有参数,然后调用parseArgs对命令行参数进行解析
            //解析的规则则是通过调用OptTable的ParseOneArg方法对字符串进行遍历解析
            //ParseOneArg内部调用了accept方法,该方法对参数别名会进行判断和特殊处理,如下:
          InputArgList Args =
          getOpts().ParseArgs(ArgStrings, MissingArgIndex, MissingArgCount,
                              IncludedFlagsBitmask, ExcludedFlagsBitmask);
                //判断参数是否支持,是否支持通过Optins.td文件进行查找,否则抛出异常
              for (const Arg *A : Args) {
                    if (A->getOption().hasFlag(options::Unsupported)) {
                  DiagID = diag::err_drv_unsupported_opt;
                        Diag(DiagID) << ArgString;
                }
      }
        
        Arg *Option::accept(const ArgList &Args,
                        unsigned &Index,
                        unsigned ArgSize) const {
        //解析参数
              std::unique_ptr<Arg> A(acceptInternal(Args, Index, ArgSize));
      if (!A)
        return nullptr;
        //判断是否有别名
          const Option &UnaliasedOption = getUnaliasedOption();
      if (getID() == UnaliasedOption.getID())
        return A.release();
        //从unalias选项中获取拼写,对应的关系在Optins.td中有声明
          StringRef UnaliasedSpelling = Args.MakeArgString(
          Twine(UnaliasedOption.getPrefix()) + Twine(UnaliasedOption.getName()));
      }
        
        void Driver::BuildUniversalActions(Compilation &C, const ToolChain &TC,
                                       const InputList &BAInputs) const {
            //2.1.根据-arch参数生成需要处理的的Archs,构建actions
            DerivedArgList &Args = C.getArgs();
            ActionList &Actions = C.getActions();
              for (Arg *A : Args) {
        if (A->getOption().matches(options::OPT_arch)) {
          //xxx
          if (ArchNames.insert(A->getValue()).second)
            Archs.push_back(A->getValue());
        }
        }
        //如果没有传入 -arch 参数,则获取 triple 对应的架构
         if (!Archs.size())
            Archs.push_back(Args.MakeArgString(TC.getDefaultUniversalArchName()));
          ActionList SingleActions;
            //调用 Driver::handleArguments 方法对参数进行处理
              BuildActions(C, Args, BAInputs, SingleActions);
        
          // 构建 offloading actions.
            OffloadingActionBuilder OffloadBuilder(C, Args, Inputs);
    
            // 构造要执行的actions
            HeaderModulePrecompileJobAction *HeaderModuleAction = nullptr;
              ActionList LinkerInputs;
              ActionList MergerInputs;
    
            for (auto &I : Inputs) {
                types::ID InputType = I.first;
                const Arg *InputArg = I.second;
                //根据输入源码文件inputs获取需要处理的 phase 数组
          //phase其实就是一个枚举类型,包含Preprocess Precompile Compile Backend Assemble Link IfsMerge
          //在 Types.def 文件维护了不同文件类型默认情况下需要经历的 phase
            llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> PL;
          types::getCompilationPhases(*this, Args, InputType, PL);
            if (PL.empty())
            continue;
                //xxx
      }
    
    //2.2.
    void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
                              const InputList &Inputs, ActionList &Actions) const {
            //将每个phase转化为一个action
        for (phases::ID Phase : PL) {
          //xxx
          // Otherwise construct the appropriate action.
          //该方法内部根据Phase的类型进行action的构建,详情在个下方法:
          Action *NewCurrent = ConstructPhaseAction(C, Args, Phase, Current);
          if (auto *HMA = dyn_cast<HeaderModulePrecompileJobAction>(NewCurrent))
            HeaderModuleAction = HMA;
          Current = NewCurrent;
          // Use the current host action in any of the offloading actions, if
          // required.
          if (OffloadBuilder.addHostDependenceToDeviceActions(Current, InputArg))
            break;
          if (Current->getType() == types::TY_Nothing)
            break;
        }
    }
      
        Action *Driver::ConstructPhaseAction(
        Compilation &C, const ArgList &Args, phases::ID Phase, Action *Input,
        Action::OffloadKind TargetDeviceOffloadKind) const {
          switch (Phase) {
      case phases::Preprocess: {
            //通过 Input 和 OutputTy 构建 PreprocessJobAction
        return C.MakeAction<PreprocessJobAction>(Input, OutputTy);
      }
        return C.MakeAction<PrecompileJobAction>(Input, OutputTy);
      }
      case phases::Compile: {
        return C.MakeAction<CompileJobAction>(Input, types::TY_LLVM_BC);
      }
          case phases::Backend: {
        if (isUsingLTO() && TargetDeviceOffloadKind == Action::OFK_None) {
          types::ID Output =
              Args.hasArg(options::OPT_S) ? types::TY_LTO_IR : types::TY_LTO_BC;
          return C.MakeAction<BackendJobAction>(Input, Output);
        }
        if (Args.hasArg(options::OPT_emit_llvm)) {
          types::ID Output =
              Args.hasArg(options::OPT_S) ? types::TY_LLVM_IR : types::TY_LLVM_BC;
          return C.MakeAction<BackendJobAction>(Input, Output);
        }
        return C.MakeAction<BackendJobAction>(Input, types::TY_PP_Asm);
      }
      case phases::Assemble:
        return C.MakeAction<AssembleJobAction>(std::move(Input), types::TY_Object);
      }
      }
        
      //3.构建jobs
      void Driver::BuildJobs(Compilation &C) const {
            //1.收集需要处理的架构
          llvm::StringSet<> ArchNames;
      if (C.getDefaultToolChain().getTriple().isOSBinFormatMachO())
        for (const Arg *A : C.getArgs())
          if (A->getOption().matches(options::OPT_arch))
            ArchNames.insert(A->getValue());
            //2.缓存action和inputinfo的映射
            //BuildJobsForAction 方法会先查找缓存,如果缓存中不存在则再调用 BuildJobsForActionNoCache 方法创建 InputInfo
          std::map<std::pair<const Action *, std::string>, InputInfo> CachedResults;
            BuildJobsForAction(C, A, &C.getDefaultToolChain(),
                           /*BoundArch*/ StringRef(),
                           /*AtTopLevel*/ true,
                           /*MultipleArchs*/ ArchNames.size() > 1,
                           /*LinkingOutput*/ LinkingOutput, CachedResults,
                           /*TargetDeviceOffloadKind*/ Action::OFK_None);       
      }
        
     InputInfo Driver::BuildJobsForActionNoCache(
        Compilation &C, const Action *A, const ToolChain *TC, StringRef BoundArch,
        bool AtTopLevel, bool MultipleArchs, const char *LinkingOutput,
        std::map<std::pair<const Action *, std::string>, InputInfo> &CachedResults,
        Action::OffloadKind TargetDeviceOffloadKind) const {
                
         if (const BindArchAction *BAA = dyn_cast<BindArchAction>(A)) {
        const ToolChain *TC;
        StringRef ArchName = BAA->getArchName();
            //3.通过 computeTargetTriple 计算 triple,然后获取合适的工具链
        // computeTargetTriple会获取-target参数更新TargetTriple字符串,然后根据其生产Triple实例
        if (!ArchName.empty())
          TC = &getToolChain(C.getArgs(),
                             computeTargetTriple(*this, TargetTriple,
                                                 C.getArgs(), ArchName));
        else
          TC = &C.getDefaultToolChain();
            //4.随后会以 BindArchAction 持有的第一个 input(类型是 LinkJobAction)为参数再次调用 BuildJobsForAction 方法
        return BuildJobsForAction(C, *BAA->input_begin(), TC, ArchName, AtTopLevel,
                                  MultipleArchs, LinkingOutput, CachedResults,
                                  TargetDeviceOffloadKind);
      }
    
       //5.获取 LinkJobAction 的 Inputs
         ActionList Inputs = A->getInputs();
    
      const JobAction *JA = cast<JobAction>(A);
      ActionList CollapsedOffloadActions;
        //6.创建 ToolSelector 的实例 TS,并调用 ToolSelector::getTool 获取支持 link 的工具
      ToolSelector TS(JA, *TC, C, isSaveTempsEnabled(),
                      embedBitcodeInObject() && !isUsingLTO());
      const Tool *T = TS.getTool(Inputs, CollapsedOffloadActions);
       
         //7.通过 BuildJobsForAction 处理 Inputs
         for (const auto *OA : CollapsedOffloadActions)
        cast<OffloadAction>(OA)->doOnEachDependence(
            /*IsHostDependence=*/BuildingForOffloadDevice,
            [&](Action *DepA, const ToolChain *DepTC, const char *DepBoundArch) {
              OffloadDependencesInputInfo.push_back(BuildJobsForAction(
                  C, DepA, DepTC, DepBoundArch, /* AtTopLevel */ false,
                  /*MultipleArchs=*/!!DepBoundArch, LinkingOutput, CachedResults,
                  DepA->getOffloadingDeviceKind()));
            });
         //xxx
           //调用 Compilation::getArgsForToolChain 进行参数转换
         llvm::Triple EffectiveTriple;
      const ToolChain &ToolTC = T->getToolChain();
      const ArgList &Args =
          C.getArgsForToolChain(TC, BoundArch, A->getOffloadingDeviceKind());
       //xxx
           if (UnbundlingResults.empty())
             //调用 darwin::Linker 的 ConstructJob 方法构建 Job
          T->ConstructJob(
              C, *JA, Result, InputInfos,
              C.getArgsForToolChain(TC, BoundArch, JA->getOffloadingDeviceKind()),
              LinkingOutput);
        else
          T->ConstructJobMultipleOutputs(
              C, *JA, UnbundlingResults, InputInfos,
              C.getArgsForToolChain(TC, BoundArch, JA->getOffloadingDeviceKind()),
              LinkingOutput);
     }
    

    相关文章

      网友评论

          本文标题:clang driver

          本文链接:https://www.haomeiwen.com/subject/akjjnltx.html