JVM 模板直譯器之如何根據位元組碼生成彙編碼?
1、背景
僅針對JVM的模板直譯器:
如何根據opcode和定址模式,將bytecode生成彙編碼。
本文的示例中所使用的位元組碼和彙編碼,請參見上篇博文:按值傳遞還是按引用?
2、定址模式
本文不打算深入展開定址模式的闡述,我們聚焦Intel的IA32-64架構的指令格式:
簡要說明下,更多的請參考intel的手冊:
– Prefixes : 用於修飾操作碼Opcode,賦予其lock、repeat等的語義.
– REX Prefix:
—- Specify GPRs and SSE registers.
—- Specify 64-bit operand size.
—- Specify extended control registers.
–Opcode:操作碼,如mov、push.
–Mod R/M:定址相關,具體見手冊。
–SIB:和Mod R/M結合起來指定定址。
–Displacement:配合Mod R/M和SIB指定定址。
–Immediate:立即數。
對上面的Opcode、Mod R/W、SIB、disp、imm如果不明白,看句彙編有個概念:
%mov %eax , %rax,-0x18(%rcx,%rbx,4)
如果這句彙編也不太明白,那麼配合下面的:
– Base + (Index ∗ Scale) + Displacement – Using all the addressing components together allows efficient
indexing of a two-dimensional array when the elements of the array are 2, 4, or 8 bytes in size.
3、合法的值(64位)
關注下這4個引數的合法取值:
• Displacement — An 8-bit, 16-bit, or 32-bit value.
• Base — The value in a 64-bit general-purpose register.
• Index — The value in a 64-bit general-purpose register.
• Scale factor — A value of 2, 4, or 8 that is multiplied by the index value.
4、Mod R/M(32位定址)
我們在後文將會用到Mod R/M位元組,所以將32位定址的格式貼在這裡:
上表的備註,其中第1條將在我們的示例中用到,所以這裡留意下:
- The [--][--] nomenclature means a SIB follows the ModR/M byte.
- The disp32 nomenclature denotes a 32-bit displacement that follows the ModR/M byte (or the SIB byte if one is present) and that is
added to the index. - The disp8 nomenclature denotes an 8-bit
5、SIB(32位定址)
同樣,因為用到了Mod R/M位元組,那麼SIB位元組也可能要用到:
6、示例
6.1、準備工作
來看個實際的例子。
下面的程式碼是生成mov彙編碼:
void Assembler::movl(Address dst, Register src) { InstructionMark im(this); prefix(dst, src); emit_int8((unsigned char)0x89); emit_operand(src, dst); }
prefix(dst,src)
就是處理prefix和REX prefix,這裡我們不關注。
emit_int8((unsigned char) 0x89)
顧名思義就是生成了一個位元組,那位元組的內容0×89代表什麼呢?
先不急,還有一句emit_operand(src,dst)
,這是一段很長的程式碼,我們大概看下:
void Assembler::emit_operand(Register reg, Register base, Register index, Address::ScaleFactor scale, int disp, RelocationHolder const& rspec, int rip_relative_correction) { relocInfo::relocType rtype = (relocInfo::relocType) rspec.type(); // Encode the registers as needed in the fields they are used in int regenc = encode(reg) << 3; int indexenc = index->is_valid() ? encode(index) << 3 : 0; int baseenc = base->is_valid() ? encode(base) : 0; if (base->is_valid()) { if (index->is_valid()) { assert(scale != Address::no_scale, "inconsistent address"); // [base + index*scale + disp] if (disp == 0 && rtype == relocInfo::none && base != rbp LP64_ONLY(&& base != r13)) { // [base + index*scale] // [00 reg 100][ss index base] /************************** * 關鍵點:關注這裡 **************************/ assert(index != rsp, "illegal addressing mode"); emit_int8(0x04 | regenc); emit_int8(scale << 6 | indexenc | baseenc); } else if (is8bit(disp) && rtype == relocInfo::none) { // ... } else { // [base + index*scale + disp32] // [10 reg 100][ss index base] disp32 assert(index != rsp, "illegal addressing mode"); emit_int8(0x84 | regenc); emit_int8(scale << 6 | indexenc | baseenc); emit_data(disp, rspec, disp32_operand); } } else if (base == rsp LP64_ONLY(|| base == r12)) { // ... } else { // ... } } else { // ... } }
上面的程式碼的關注點已經標出,這裡我們將其抽出,並將前文中的emit_int8((unsigned char) 0x89)
結合起來:
emit_int8((unsigned char) 0x89) emit_int8(0x04 | regenc); emit_int8(scale << 6 | indexenc | baseenc);
最終其生成了如下的彙編程式碼(64位機器):
mov %eax,(%rcx,%rbx,1)
好了,問題來了:
上面這句彙編怎麼得出的?
6.2、計算過程
我們給個下面的值:
regenc = 0x0,scale << 6 | indexenc | baseenc = 25
進行簡單的運算就可以得到:
emit_int8((unsigned char) 0x89) //得到0x89 emit_int8(0x04 | regenc); //得到0x04 emit_int8(scale << 6 | indexenc | baseenc); //得到0x19
合起來就是三個位元組:
0x89 0x04 0x19
1、0×89對應什麼?
從上表可以看出因為JVM工作在64位下,所以需要配合REX.W來“起頭”,不過在我們這個例子中,其恰好是0。
主要看那個89/r:
MOV r/m64,r64 //64位,將暫存器中的值給到暫存器或者記憶體地址中
2、0×04代表什麼?
現在我們要用到上面的Mod R/M表和SIB表了。
用第二個位元組0×04查Mod R/M表,可知源運算元是暫存器EAX,同時可知定址型別是[--][--]型別,含義為:
The [--][--] nomenclature means a SIB follows the ModR/M byte.
3、0×19代表什麼?
繼續查SIB表,對應位元組0×19的是:
base = ECX scaled index = EBX
4、彙編程式碼:
//32位 mov %eax,%(ecx,ebx,1) //64位 mov %rax,%(rcx,rbx,1)
7、結語
本文簡要探討了:
如何根據opcode和定址模式,將bytecode生成彙編碼。
相關文章
- 【JVM原始碼解析】模板直譯器解釋執行Java位元組碼指令(上)JVM原始碼Java
- python反編譯之位元組碼Python編譯
- Java程式碼如何檢視位元組碼及彙編碼Java
- JWebAssembly:Java 位元組碼到 WebAssembly 編譯器WebJava編譯
- 深入底層|JVM原始碼解讀:HotSpot的模板直譯器JVM原始碼HotSpot
- 根據使用者編號生成邀請碼
- 淺談彙編器、編譯器和直譯器編譯
- 【Java】JVM位元組碼分析JavaJVM
- 根據URL地址生成二維碼
- Python 根據id生成唯一碼Python
- 根據api檔案生成程式碼API
- 深入淺出JVM(十)之位元組碼指令(下篇)JVM
- Clojure 執行原理之位元組碼生成篇
- Needle:基於 DFA 的正規表示式庫,可編譯為 JVM 位元組碼編譯JVM
- 編碼、摘要和加密(一)——位元組編碼加密
- 要點提煉| 理解JVM之位元組碼執行引擎JVM
- spring boot itextPdf根據模板生成pdf檔案Spring Boot
- Vue 原始碼解讀(10)—— 編譯器 之 生成渲染函式Vue原始碼編譯函式
- P001-根據編碼規則自動生成ID的函式函式
- 機器學習根據文字生成圖片教程(附python程式碼)機器學習Python
- OpenAPI Generator,根據Swagger/OpenAPI生成程式碼的工具APISwagger
- mybatis根據表逆向自動化生成程式碼MyBatis
- EF3.1 根據資料庫生成程式碼資料庫
- Vue3原始碼分析——編譯模組和編譯器Vue原始碼編譯
- freemarker根據靜態模板和動態模板生成PDF與Word
- Flutter編譯時生成程式碼之 code_builderFlutter編譯UI
- 深入理解JVM位元組碼執行引擎JVM
- jvm位元組碼和類載入機制JVM
- JVM(四):深入分析Java位元組碼-下JVMJava
- JVM(三):深入分析Java位元組碼-上JVMJava
- 玩命學JVM(一)—認識JVM和位元組碼檔案JVM
- 詳解Android Gradle生成位元組碼流程AndroidGradle
- 淺談Kotlin語法篇之lambda編譯成位元組碼過程完全解析(七)Kotlin編譯
- Java 根據模板生成 PDF 檔案 以及 excel 檔案JavaExcel
- 深入理解泛型-重寫泛型類方法遇到的問題(涉及JVM反編譯位元組碼)泛型JVM編譯
- 一夜搞懂 | JVM 位元組碼執行引擎JVM
- 位元組碼
- 要點提煉| 理解JVM之程式編譯&程式碼優化JVM編譯優化