ART世界探险(14) - 快速编译器和优化编译器

ART世界探险(14) - 快速编译器和优化编译器

ART的编译器为两种,一种是QuickCompiler,快速编译器;另一种是OptimizingCompiler,优化编译器。

编译器的基类 - Compiler类

Compiler类是真正实现Java方法和Jni方法编译的入口。
我们先通过一个思维导图来看一下它的结构:

有了上面的结构图之后,我们再看下面的类结构就非常清晰了。

class Compiler {
 public:
  enum Kind {
    kQuick,
    kOptimizing
  };

Kind有两类,Quick和Optimizing。它的子类也有两个:QuickCompiler和OptimizingCompiler.

  static Compiler Create(CompilerDriver driver, Kind kind);

  virtual void Init() = 0;

  virtual void UnInit() const = 0;

  virtual bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu)
      const = 0;

  virtual CompiledMethod Compile(const DexFile::CodeItem code_item,
                                  uint32_t access_flags,
                                  InvokeType invoke_type,
                                  uint16_t class_def_idx,
                                  uint32_t method_idx,
                                  jobject class_loader,
                                  const DexFile& dex_file) const = 0;

  virtual CompiledMethod* JniCompile(uint32_t access_flags,
                                     uint32_t method_idx,
                                     const DexFile& dex_file) const = 0;

  virtual uintptr_t GetEntryPointOf(ArtMethod* method) const
     SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) = 0;

  uint64_t GetMaximumCompilationTimeBeforeWarning() const {
    return maximum_compilation_time_before_warning_;
  }

  virtual void InitCompilationUnit(CompilationUnit& cu) const = 0;

  virtual ~Compiler() {}

  /*
   * @brief Generate and return Dwarf CFI initialization, if supported by the
   * backend.
   * @param driver CompilerDriver for this compile.
   * @returns nullptr if not supported by backend or a vector of bytes for CFI DWARF
   * information.
   * @note This is used for backtrace information in generated code.
   */
  virtual std::vector<uint8_t>* GetCallFrameInformationInitialization(const CompilerDriver& driver)
      const {
    UNUSED(driver);
    return nullptr;
  }

  // Returns whether the method to compile is such a pathological case that
  // it's not worth compiling.
  static bool IsPathologicalCase(const DexFile::CodeItem& code_item,
                                 uint32_t method_idx,
                                 const DexFile& dex_file);

 protected:
  explicit Compiler(CompilerDriver* driver, uint64_t warning) :
      driver_(driver), maximum_compilation_time_before_warning_(warning) {
  }

  CompilerDriver* GetCompilerDriver() const {
    return driver_;
  }

 private:
  CompilerDriver* const driver_;
  const uint64_t maximum_compilation_time_before_warning_;

  DISALLOW_COPY_AND_ASSIGN(Compiler);
};

快速编译器 - QuickCompiler

QuickerCompiler在实现了所有Compiler基类的方法之外,新增了两个PassManager,用来管理前优化的Pass和后优化的Pass.
我们来看下增加了QuickCompiler之后的Compiler思维导图:

class QuickCompiler : public Compiler {
 public:
  virtual ~QuickCompiler();

  void Init() OVERRIDE;

  void UnInit() const OVERRIDE;

  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
      OVERRIDE;

  CompiledMethod Compile(const DexFile::CodeItem code_item,
                          uint32_t access_flags,
                          InvokeType invoke_type,
                          uint16_t class_def_idx,
                          uint32_t method_idx,
                          jobject class_loader,
                          const DexFile& dex_file) const OVERRIDE;

  CompiledMethod* JniCompile(uint32_t access_flags,
                             uint32_t method_idx,
                             const DexFile& dex_file) const OVERRIDE;

  uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);

  static Mir2Lir GetCodeGenerator(CompilationUnit cu, void* compilation_unit);

  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;

  static Compiler Create(CompilerDriver driver);

  const PassManager* GetPreOptPassManager() const {
    return pre_opt_pass_manager_.get();
  }
  const PassManager* GetPostOptPassManager() const {
    return post_opt_pass_manager_.get();
  }

 protected:
  explicit QuickCompiler(CompilerDriver* driver);

 private:
  std::unique_ptr<PassManager> pre_opt_pass_manager_;
  std::unique_ptr<PassManager> post_opt_pass_manager_;
  DISALLOW_COPY_AND_ASSIGN(QuickCompiler);
};

PassManager

我们顺藤摸瓜来看下PassManager的作用:

class PassManager {
 public:
  explicit PassManager(const PassManagerOptions& options);
  virtual ~PassManager();
  void CreateDefaultPassList();
  void AddPass(const Pass* pass) {
    passes_.push_back(pass);
  }
  /**
   * @brief Print the pass names of all the passes available.
   */
  void PrintPassNames() const;
  const std::vector<const Pass> GetDefaultPassList() const {
    return &default_pass_list_;
  }
  const PassManagerOptions& GetOptions() const {
    return options_;
  }

 private:
  /* @brief The set of possible passes.  /
  std::vector<const Pass*> passes_;

  /* @brief The default pass list is used to initialize pass_list_. /
  std::vector<const Pass*> default_pass_list_;

  /* @brief Pass manager options. /
  PassManagerOptions options_;

  DISALLOW_COPY_AND_ASSIGN(PassManager);
};

从上面的类中可以看出,PassManager主要就是Pass的一个列表容器。

Pass是什么?

上面是Pass的列表,那么Pass是什么呢?
Pass就是优化时,我们所要做的一些步骤。这次是我们第一次跟它打交道,后面我们要花一些时间在Pass上。
我们先看看,ART都提供了哪些Pass供我们选择:

Pass有两个重要的子类,一个是PassME,目前所有的其它子类都继承自PassME。另一个是PassME的子类PassMEMirSsaRep,是将MIR进行SSA表示的优化。

优化编译器OptimizingCompiler

优化编译器在Compiler的基础上增加了两个公开方法:
1. TryCompile
2. MaybeRecordStat

另外,在私有方法上,优化编译器区分了带有优化的编译CompileOptimized和不带优化的CompileBaseline.
我们再看下思维导图,加深一下印象:

下面是源代码,大家浏览过一下就好。

class OptimizingCompiler FINAL : public Compiler {
 public:
  explicit OptimizingCompiler(CompilerDriver* driver);
  ~OptimizingCompiler();

  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
      OVERRIDE;

  CompiledMethod Compile(const DexFile::CodeItem code_item,
                          uint32_t access_flags,
                          InvokeType invoke_type,
                          uint16_t class_def_idx,
                          uint32_t method_idx,
                          jobject class_loader,
                          const DexFile& dex_file) const OVERRIDE;

  CompiledMethod TryCompile(const DexFile::CodeItem code_item,
                             uint32_t access_flags,
                             InvokeType invoke_type,
                             uint16_t class_def_idx,
                             uint32_t method_idx,
                             jobject class_loader,
                             const DexFile& dex_file) const;

  CompiledMethod* JniCompile(uint32_t access_flags,
                             uint32_t method_idx,
                             const DexFile& dex_file) const OVERRIDE {
    return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
  }

  uintptr_t GetEntryPointOf(ArtMethod* method) const OVERRIDE
      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
    return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCodePtrSize(
        InstructionSetPointerSize(GetCompilerDriver()->GetInstructionSet())));
  }

  void InitCompilationUnit(CompilationUnit& cu) const OVERRIDE;

  void Init() OVERRIDE;

  void UnInit() const OVERRIDE;

  void MaybeRecordStat(MethodCompilationStat compilation_stat) const {
    if (compilation_stats_.get() != nullptr) {
      compilation_stats_->RecordStat(compilation_stat);
    }
  }

 private:
  // Whether we should run any optimization or register allocation. If false, will
  // just run the code generation after the graph was built.
  const bool run_optimizations_;

  // Optimize and compile `graph`.
  CompiledMethod CompileOptimized(HGraph graph,
                                   CodeGenerator* codegen,
                                   CompilerDriver* driver,
                                   const DexFile& dex_file,
                                   const DexCompilationUnit& dex_compilation_unit,
                                   PassInfoPrinter* pass_info) const;

  // Just compile without doing optimizations.
  CompiledMethod CompileBaseline(CodeGenerator codegen,
                                  CompilerDriver* driver,
                                  const DexCompilationUnit& dex_compilation_unit) const;

  std::unique_ptr<OptimizingCompilerStats> compilation_stats_;

  std::unique_ptr<std::ostream> visualizer_output_;

  // Delegate to Quick in case the optimizing compiler cannot compile a method.
  std::unique_ptr<Compiler> delegate_;

  DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
};

优化编译状态 - MethodComplicationStat

优化编译还是挺复杂的,有下面一堆状态:
后面用到的时候我们再详细解释,大家知道有一堆状态就好。

enum MethodCompilationStat {
  kAttemptCompilation = 0,
  kCompiledBaseline,
  kCompiledOptimized,
  kCompiledQuick,
  kInlinedInvoke,
  kInstructionSimplifications,
  kNotCompiledBranchOutsideMethodCode,
  kNotCompiledCannotBuildSSA,
  kNotCompiledCantAccesType,
  kNotCompiledClassNotVerified,
  kNotCompiledHugeMethod,
  kNotCompiledLargeMethodNoBranches,
  kNotCompiledMalformedOpcode,
  kNotCompiledNoCodegen,
  kNotCompiledNonSequentialRegPair,
  kNotCompiledPathological,
  kNotCompiledSpaceFilter,
  kNotCompiledUnhandledInstruction,
  kNotCompiledUnresolvedField,
  kNotCompiledUnresolvedMethod,
  kNotCompiledUnsupportedIsa,
  kNotCompiledVerifyAtRuntime,
  kNotOptimizedDisabled,
  kNotOptimizedRegisterAllocator,
  kNotOptimizedTryCatch,
  kRemovedCheckedCast,
  kRemovedDeadInstruction,
  kRemovedNullCheck,
  kLastStat
};

编译单元:CompilationUnit

我们先看下CompliationUnit的思维导图结构:

struct CompilationUnit {
  CompilationUnit(ArenaPool* pool, InstructionSet isa, CompilerDriver* driver, ClassLinker* linker);
  ~CompilationUnit();

  void StartTimingSplit(const char* label);
  void NewTimingSplit(const char* label);
  void EndTiming();

  /*
   * Fields needed/generated by common frontend and generally used throughout
   * the compiler.
  */
  CompilerDriver* const compiler_driver;
  ClassLinker* const class_linker;        // Linker to resolve fields and methods.
  const DexFile* dex_file;                // DexFile containing the method being compiled.
  jobject class_loader;                   // compiling method's class loader.
  uint16_t class_def_idx;                 // compiling method's defining class definition index.
  uint32_t method_idx;                    // compiling method's index into method_ids of DexFile.
  uint32_t access_flags;                  // compiling method's access flags.
  InvokeType invoke_type;                 // compiling method's invocation type.
  const char* shorty;                     // compiling method's shorty.
  uint32_t disable_opt;                   // opt_control_vector flags.
  uint32_t enable_debug;                  // debugControlVector flags.
  bool verbose;
  const InstructionSet instruction_set;
  const bool target64;

  // TODO: move memory management to mir_graph, or just switch to using standard containers.
  ArenaAllocator arena;
  ArenaStack arena_stack;  // Arenas for ScopedArenaAllocator.

  std::unique_ptr<MIRGraph> mir_graph;   // MIR container.
  std::unique_ptr<Mir2Lir> cg;           // Target-specific codegen.
  TimingLogger timings;
  bool print_pass;                 // Do we want to print a pass or not?

  /**
   * @brief Holds pass options for current pass being applied to compilation unit.
   * @details This is updated for every pass to contain the overridden pass options
   * that were specified by user. The pass itself will check this to see if the
   * default settings have been changed. The key is simply the option string without
   * the pass name.
   */
  SafeMap<const std::string, const OptionContent> overridden_pass_options;
};
时间: 2024-11-01 14:26:15

ART世界探险(14) - 快速编译器和优化编译器的相关文章

ART世界探险(16) - 快速编译器下的方法编译

ART世界探险(16) - 快速编译器下的方法编译 我们对三大组件有了了解之后,下面终于可以开始正餐,开始分析两种Compiler下的Compile函数. 我们先看一张图,对于整个流程有个整体的印象,然后我们再去看代码: QuickCompiler的Compile CompiledMethod QuickCompiler::Compile(const DexFile::CodeItem code_item, uint32_t access_flags, InvokeType invoke_typ

ART世界探险(19) - 优化编译器的编译流程

ART世界探险(19) - 优化编译器的编译流程 前面,我们对于快速编译器的知识有了一点了解,对于CompilerDriver,MIRGraph等都有了初步的印象. 下面,我们回头看一下优化编译器的编译过程.有了前面的基础,后面的学习过程会更顺利一些. 下面我们先看个地图,看看我们将遇到哪些新的对象: OptimizingCompiler::Compile 我们先来看看优化编译的入口点,Compile函数: CompiledMethod OptimizingCompiler::Compile(c

ART世界探险(7) - 数组

ART世界探险(7) - 数组 Java针对数据是有专门的指令去处理的,这与C/C++有显著的不同. Java字节码对于数组的支持 一个极简的例子 Java源代码 为了简化,我们取一个极简的例子来说明Java的数组指令的用法: 我们new一个长度为1的字节数组,然后返回这个数组的长度. public static int testByteArrayLength(){ byte[] baArray = new byte[1]; return baArray.length; } Java字节码 有几

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中)

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中) 段(section)的概念 一块内存分配给应用程序之后,从代码的组织上,我们就有将它们分段的需求. 比如,可以分为代码段,数据段,只读数据段,堆栈段,未初始化的数据段等等. 在GAS汇编器中,我们通过.section伪指令来指定段名.ARM编译器我买不起,我就忽略它了. 标准section 段的描述 默认段名 代码段 .text 经过初始化的数据段 .data 未经初始化的数据段 .bss BSS是Block Star

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件 CompilerDriver 调用编译器的接口是CompilerDriver. 我们看一看CompilerDriver的结构图吧: 这是我们在ART里能遇见的第一个复杂的大类.但凡编译相关,都要通过它来打交道.结果,它就把自己搞成了一个大杂烩. ClassLinker Java是门面向对象的语言,导致类相关的操作比较复杂. 在应用层有ClassLoader,在运行环境层就有ClassLink

ART世界探险(6) - 流程控制指令

ART世界探险(6) - 流程控制指令 分支结构 Java分支结构 我们先来个最简单的,比较大小吧. public static long bigger(long a, long b){ if(a>=b){ return a; }else{ return b; } } public static int less(int a,int b){ if(a<=b){ return a; }else{ return b; } } 看看Java字节码是个什么样子: public static long

ART世界探险(9) - 同步锁

ART世界探险(9) - 同步锁 Java是一种把同步锁写进语言和指令集的语言. 从语言层面,Java提供了synchronized关键字. 从指令集层面,Java提供了monitorenter和monitorexit两条指令. 下面我们就看看它们是如何实现的吧. 三种锁的方式 Java代码 有三种方式来加锁: 直接在函数上加synchronized关键字 在函数内用某Object去做同步 调用concurrent库中的其他工具 public synchronized int newID(){

ART世界探险(3) - ARM 64位CPU的架构快餐教程

ART世界探险(3) - ARM 64位CPU的架构快餐教程 前面我们说过,Dalvik如果没有JIT的话,可以做到架构无关,让Dalvik指令都解释执行.但是ART是AOT,要编译成针对芯片具体的机器指令. 所以,研究Dalvik的时候可以不用太关心目标指令,而我们研究ART必须对目前最流行的微处理器的架构有个基本的了解. 在上一讲我们对于ART从java byte code到ARM64 v8指令的整个流程有了一个大概的了解之后,我们就目前最流行的ARM64位芯片的知识进行一些探索. 我们的目

ART世界探险(11) - OAT文件格式分析(1) - ELF文件头(上)

ART世界探险(11) - OAT文件格式分析(1) - ELF文件头(上) 既然是要探险,咱们就保持一定的深度,起码将来可以做个基于ART的黑客之类的. 所以我们针对细节多下一些工夫,先仔细分析一下OAT文件的格式. ART的本质上是一个编译器,所以我们需要对编译.链接的主要环节都有一个比较深入的了解.想要绕过编译原理去学好ART,是不太现实的一件事情,我们选择可以让这个过程有趣和好玩. 闲扯不多说了,言归正传. 可执行文件 OAT是一种可执行文件,所以封装在一个ELF格式的可执行文件中. 可