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的基礎上增加了兩個公開方法:
- TryCompile
- 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;
};