04.26 淺談把Java字節碼譯為C代碼的意義

Java字節碼是基於棧的一種編碼。這種編碼方式十分方便解釋器的設計,但同時不利於程序分析,因此一些高效的代碼優化技術無法方便的Java字節碼上實現。

先大體說說Java字節碼的特點。目前版本的Java大概有200+的字節碼指令,其中大部分都是1字節指令,這也是為什麼叫做字節碼。少部分指令是多字節或不定長指令。對於解釋器來說,解釋指令時一般都是在操作兩個區域。一個是棧,一個是局部變量表。舉例來說,iload1指令,就是從局部變量表的1號槽位的數據放入操作數棧中,即*stack++ = locals。

與C或者其他常用的編程語言不同的是,Java字節碼的操作數類型是隱含的,操作的類型的顯示的,而C語言中操作數類型都是顯示的,但是操作是多態的。比如“+”,在C語言中“+”兩邊的操作數類型可以是int型,可以是double。尚學堂•百戰程序員陳老師指出Java字節碼中iadd指令明確表示了要操作相加的兩個數一定是int型。但是當拋開iadd指令而直接觀測操作數棧時,並不知道棧上操作數的類型。

淺談把Java字節碼譯為C代碼的意義

Java字節碼在每一條指令執行時,操作數棧的深度,局部變量表的大小,以及它們上面的操作數類型都是可以確定的。而且,無論從何種路徑執行到某一條指令,操作數棧深度及操作數類型都是確定的,以一個簡單的a=b+c的例子來說明這個翻譯過程。

其中局部變量的類型都是已知的。可以看到s0,s1跟Java操作數棧的功能一樣,是為了存放臨時的計算結果。上面的代碼完全可以化簡為“l1 = l1 + l2”。但前期沒有必要引入這種複雜性,這種化簡完全可以由後續的各種優化完成。

上面的例子實際上的存在一些問題的。雖然Java操作數棧和局部變量表裡面存放的數據都是有類型,但是棧和局部變量表本身只是一個存儲空間罷了,並沒有規定裡面必須存放什麼類型的數據。所以每次在給棧空間或者局部變量賦值的時候,我們有必要新聲明一個局部變量。

通過數據流分析可以求出def-use,方便做上面的這種變量分裂,這裡不詳細說了。最近在研究Soot,由於Soot的目的是對字節碼做優化,所以裡面也有將字節碼翻譯為Jimple的邏輯。但是不明白Soot為什麼需要類型推導,目前我感覺將Java字節碼翻譯為Jimple完全不需要推導類型。

淺談把Java字節碼譯為C代碼的意義


分享到:


相關文章: