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;
};