ART世界探險(15) - Compiler,ClassLinker,Runtime三大組件
CompilerDriver
調(diào)用編譯器的接口是CompilerDriver愕贡。
我們看一看CompilerDriver的結(jié)構(gòu)圖吧:
這是我們在ART里能遇見的第一個復(fù)雜的大類。但凡編譯相關(guān)徙瓶,都要通過它來打交道昆庇。結(jié)果躲胳,它就把自己搞成了一個大雜燴上沐。
ClassLinker
Java是門面向?qū)ο蟮恼Z言婶芭,導(dǎo)致類相關(guān)的操作比較復(fù)雜乘陪。
在應(yīng)用層有ClassLoader,在運行環(huán)境層就有ClassLinker雕擂。
我們看一下ClassLinker的公開方法啡邑,私有的還有同樣多的,汗井赌。
ClassLinker相對于CompilerDriver谤逼,邏輯上更為集中一些。
它主要是提供跟類相關(guān)的操作仇穗,包括類級的分配對象等流部。
CompilerDriver提供的主要是編譯期底層代碼的功能,而ClassLinker在面向?qū)ο蟮倪壿媽犹峁┓?wù)纹坐。
Runtime
ART是Android Runtime的縮寫枝冀,我們終于可以揭開Android Runtime的面紗了。
Runtime主要是提供一些運行時的服務(wù)耘子,最重要的當然就是GC果漾。另外,還有多線程和線程安全相關(guān)的支持谷誓,事務(wù)相關(guān)的支持等绒障。
有了上面三個大組件的支持,不管是編譯期還是運行時捍歪,我們都可以找到支持Java方法運行的基礎(chǔ)設(shè)施户辱。
最后鸵钝,我們再復(fù)習(xí)一下上節(jié)最后出現(xiàn)的編譯單元類:
CompilationUnit的作用是連接前端和后端。
將前端的DexFile通過CompilerDriver進行編譯之后庐镐,我們先得到中間層中間代碼MIR恩商,MIRGraph就是這一步要做的工作。很多優(yōu)化也是在這一步完成的必逆。
然后痕届,再通過Mir2Lir,將MIR轉(zhuǎn)化成更接近于機器指令的低層中間代碼LIR末患。
最后研叫,再將LIR落地成目標機器的指令。
dex2oat編譯流程(續(xù))
首先我們復(fù)習(xí)一下之前學(xué)到的璧针,dex2oat做為入口點嚷炉,會調(diào)用CompilerDriver的方法對dex文件進行編譯。
下面該開始CompilerDriver的CompileClass探橱,看了CompilerDriver的大圖之后申屹,對于它是不是更親切了呢?
CompileClass
編譯類的重頭戲還在于編譯方法隧膏。
CompileClass類的主要邏輯哗讥,就是針對直接方法和虛擬方法,分別遍歷然后編譯胞枕。
我們將前面的判斷和校驗等細節(jié)都略過杆煞,這個函數(shù)的框架如下面所示:
void CompilerDriver::CompileClass(const ParallelCompilationManager* manager,
size_t class_def_index) {
...
CompilerDriver* const driver = manager->GetCompiler();
...
// Compile direct methods
int64_t previous_direct_method_idx = -1;
while (it.HasNextDirectMethod()) {
uint32_t method_idx = it.GetMemberIndex();
if (method_idx == previous_direct_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
// http://code.google.com/p/smali/issues/detail?id=119
it.Next();
continue;
}
previous_direct_method_idx = method_idx;
driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
compilation_enabled);
it.Next();
}
// Compile virtual methods
int64_t previous_virtual_method_idx = -1;
while (it.HasNextVirtualMethod()) {
uint32_t method_idx = it.GetMemberIndex();
if (method_idx == previous_virtual_method_idx) {
// smali can create dex files with two encoded_methods sharing the same method_idx
// http://code.google.com/p/smali/issues/detail?id=119
it.Next();
continue;
}
previous_virtual_method_idx = method_idx;
driver->CompileMethod(self, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
it.GetMethodInvokeType(class_def), class_def_index,
method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
compilation_enabled);
it.Next();
}
DCHECK(!it.HasNext());
}
CompileMethod
從這里開始,我們終于深入到可以生成代碼的程度了腐泻。
void CompilerDriver::CompileMethod(Thread* self, 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,
DexToDexCompilationLevel dex_to_dex_compilation_level,
bool compilation_enabled) {
CompiledMethod* compiled_method = nullptr;
uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0;
MethodReference method_ref(&dex_file, method_idx);
首先是對JNI調(diào)用的處理决乎,我們之前曾經(jīng)看到過的序列。這里會調(diào)用JniCompile函數(shù)派桩。下面開始處理JNI:
if ((access_flags & kAccNative) != 0) {
// Are we interpreting only and have support for generic JNI down calls?
if (!compiler_options_->IsCompilationEnabled() &&
InstructionSetHasGenericJniStub(instruction_set_)) {
// Leaving this empty will trigger the generic JNI version
} else {
compiled_method = compiler_->JniCompile(access_flags, method_idx, dex_file);
CHECK(compiled_method != nullptr);
}
抽象方法不需要生成代碼:
} else if ((access_flags & kAccAbstract) != 0) {
// Abstract methods don't have code.
下面再開始編普通方法构诚,通過調(diào)用Compile方法來完成。
} else {
bool has_verified_method = verification_results_->GetVerifiedMethod(method_ref) != nullptr;
bool compile = compilation_enabled &&
// Basic checks, e.g., not <clinit>.
verification_results_->IsCandidateForCompilation(method_ref, access_flags) &&
// Did not fail to create VerifiedMethod metadata.
has_verified_method &&
// Is eligable for compilation by methods-to-compile filter.
IsMethodToCompile(method_ref);
if (compile) {
// NOTE: if compiler declines to compile this method, it will return null.
compiled_method = compiler_->Compile(code_item, access_flags, invoke_type, class_def_idx,
method_idx, class_loader, dex_file);
}
...
}
如上一講我們所介紹的铆惑,ART有兩種Compiler范嘱,QuickCompiler和OptimizationCompiler。
所以员魏,根據(jù)dex2oat參數(shù)的不同丑蛤,分別調(diào)用這兩種Compiler的Compile方法來實現(xiàn)真正的編譯。
我們看一個圖來復(fù)習(xí)一下: