爲何要將Java字節碼翻譯爲C代碼?算法
Java字節碼是基於棧的一種編碼。這種編碼方式十分方便解釋器的設計,但同時不利於程序分析,所以一些高效的代碼優化技術沒法方便的Java字節碼上實現。編程
先大致說說Java字節碼的特色。目前版本的Java大概有200+的字節碼指令,其中大部分都是1字節指令,這也是爲何叫作字節碼。少部分指令是多字節或不定長指令。編程語言
對於解釋器來講,解釋指令時通常都是在操做兩個區域。一個是棧,一個是局部變量表。舉例來講,iload1指令,就是從局部變量表的1號槽位的數據放入操做數棧中,即*stack++ = locals[1]。優化
與C或者其餘經常使用的編程語言不一樣的是,Java字節碼的操做數類型是隱含的,操做的類型的顯示的,而C語言中操做數類型都是顯示的,可是操做是多態的。好比「+」,在C語言中「+」兩邊的操做數類型能夠是int型,能夠是double。Java字節碼中iadd指令明確表示了要操做相加的兩個數必定是int型。可是當拋開iadd指令而直接觀測操做數棧時,並不知道棧上操做數的類型。編碼
直接說結論。翻譯
Java字節碼在每一條指令執行時,操做數棧的深度,局部變量表的大小,以及它們上面的操做數類型都是能夠肯定的。並且,不管從何種路徑執行到某一條指令,操做數棧深度及操做數類型都是肯定的[1]。Java虛擬機規範的4.10.2章節介紹了字節碼校驗的一個算法,能夠參考。設計
以一個簡單的a=b+c的例子來講明這個翻譯過程。code
對應的Java字節碼以下:虛擬機
iload1 iload2 iadd istore1
咱們能夠暫時將操做數棧和局部變量表的每個槽位當作一個局部變量。上面的代碼就翻譯爲:io
s0 = l1; s1 = l2; s0 = s0 + s1; l1 = s0;
其中局部變量的類型都是已知的。能夠看到s0,s1跟Java操做數棧的功能同樣,是爲了存放臨時的計算結果。上面的代碼徹底能夠化簡爲「l1 = l1 + l2」。但前期沒有必要引入這種複雜性,這種化簡徹底能夠由後續的各類優化完成。
上面的例子實際上的存在一些問題的。雖然Java操做數棧和局部變量表裏面存放的數據都是有類型,可是棧和局部變量表自己只是一個存儲空間罷了,並無規定裏面必須存放什麼類型的數據。因此每次在給棧空間或者局部變量賦值的時候,咱們有必要新聲明一個局部變量。上面的例子翻譯爲:
s0 = l1; s1 = l2; s0_1 = s0 + s1; l1_1 = s0;
經過數據流分析能夠求出def-use,方便作上面的這種變量分裂,這裏不詳細說了。
最近在研究Soot,因爲Soot的目的是對字節碼作優化,因此裏面也有將字節碼翻譯爲Jimple的邏輯。可是不明白Soot爲何須要類型推導,目前我感受將Java字節碼翻譯爲Jimple徹底不須要推導類型。
[1] Toba: Java For ApplicationsA Way Ahead of Time (WAT) Compiler