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

简介: ART的编译器为两种,一种是QuickCompiler,快速编译器;另一种是OptimizingCompiler,优化编译器。 下面我们就分析一下这两种编译器的基本结构。

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

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

编译器的基类 - Compiler类

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

art_Compiler

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

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思维导图:

art_Compiler_v2

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

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

优化编译器OptimizingCompiler

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

  1. TryCompile
  2. MaybeRecordStat

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

Compiler_v3

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

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的思维导图结构:

art_CompilationUnit

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;
};
目录
相关文章
|
13天前
|
算法 编译器 C语言
《C 语言预处理指令:代码编译前的 “魔法棒”》
《C 语言预处理指令:代码编译前的 “魔法棒”》介绍了 C 语言中预处理指令的作用和使用方法,如宏定义、文件包含等,是编程初学者了解代码编译前处理过程的必备指南。
51 12
|
前端开发 IDE 编译器
2023-5-20-各种编译器的全面学习
2023-5-20-各种编译器的全面学习
232 0
|
前端开发 C语言 iOS开发
基于LLVM的编译原理简明教程 (1) - 写编译器越来越容易了
跟学院派的厚书给大家的印象不同,其实用LLVM写个简单的编译器是件容易的事情,因为大部分事情LLVM都替我们做了。
8341 0
|
缓存 监控 Java
看了这篇【JIT编译器】,你也能说你会java性能优化了!
本文主要介绍 java性能分析 之 JIT编译器
345 0
|
缓存 JavaScript Java
说说 方舟编译器
说说 方舟编译器
说说 方舟编译器