第35篇-方法調用指令之invokespecial與invokestatic


這一篇將詳細介紹invokespecial和invokestatic字節碼指令的匯編實現邏輯

1、invokespecial指令

invokespecial指令的模板定義如下:

def(Bytecodes::_invokespecial       , ubcp|disp|clvm|____, vtos, vtos, invokespecial       , f1_byte      );

生成函數為invokespecial(),生成的匯編代碼如下:

0x00007fffe1022250: mov    %r13,-0x38(%rbp)
0x00007fffe1022254: movzwl 0x1(%r13),%edx
0x00007fffe1022259: mov    -0x28(%rbp),%rcx
0x00007fffe102225d: shl    $0x2,%edx
0x00007fffe1022260: mov    0x10(%rcx,%rdx,8),%ebx
// 獲取ConstantPoolCacheEntry中indices[b2,b1,constant pool index]中的b1
0x00007fffe1022264: shr    $0x10,%ebx
0x00007fffe1022267: and    $0xff,%ebx
// 檢查invokespecial=183的bytecode是否已經連接,如果已經連接就進行跳轉
0x00007fffe102226d: cmp    $0xb7,%ebx
0x00007fffe1022273: je     0x00007fffe1022312

// ... 省略調用InterpreterRuntime::resolve_invoke()函數
// 對invokespecial=183的bytecode進行連接,
// 因為字節碼指令還沒有連接

// 將invokespecial x中的x加載到%edx中
0x00007fffe1022306: movzwl 0x1(%r13),%edx
// 將ConstantPoolCache的首地址存儲到%rcx中
0x00007fffe102230b: mov    -0x28(%rbp),%rcx
// %edx中存儲的是ConstantPoolCacheEntry項的索引,轉換為字偏移
0x00007fffe102230f: shl    $0x2,%edx

// 獲取ConstantPoolCache::_f1屬性的值
0x00007fffe1022312: mov    0x18(%rcx,%rdx,8),%rbx 
// 獲取ConstantPoolCache::_flags屬性的值
0x00007fffe1022317: mov    0x28(%rcx,%rdx,8),%edx 


// 將flags移動到ecx中
0x00007fffe102231b: mov    %edx,%ecx
// 從flags中取出參數大小
0x00007fffe102231d: and    $0xff,%ecx
// 獲取到recv,%rcx中保存的是參數大小,最終計算為 %rsp+%rcx*8-0x8,
// flags中的參數大小可能對實例方法來說,已經包括了recv的大小
// 如調用實例方法的第一個參數是this(recv)
0x00007fffe1022323: mov    -0x8(%rsp,%rcx,8),%rcx 
// 從flags中獲取return type,也就是從_flags的高4位保存的TosState
0x00007fffe1022328: shr    $0x1c,%edx
// 將TemplateInterpreter::invoke_return_entry地址存儲到%r10
0x00007fffe102232b: movabs $0x7ffff73b6380,%r10   
// 找到對應return type的invoke_return_entry的地址
0x00007fffe1022335: mov    (%r10,%rdx,8),%rdx     
// 通過invokespecial指令調用函數后的返回地址
0x00007fffe1022339: push   %rdx     
              
// 空值檢查
0x00007fffe102233a: cmp    (%rcx),%rax  

// ...

// 設置調用者棧頂
0x00007fffe102235c: lea    0x8(%rsp),%r13
// 向棧中last_sp的位置保存調用者棧頂
0x00007fffe1022361: mov    %r13,-0x10(%rbp)

// 跳轉到Method::_from_interpretered_entry入口去執行
0x00007fffe1022365: jmpq   *0x58(%rbx)  

invokespecial指令在調用private和構造方法時,不需要動態分發。在這個字節碼指令解析完成后,ConstantPoolCacheEntry中的_f1指向目標方法的Method實例,_f2沒有使用,所以如上匯編的邏輯非常簡單,這里不再過多介紹。 

2、invokestatic指令 

invokestatic指令的模板定義如下: 

def(Bytecodes::_invokestatic        , ubcp|disp|clvm|____, vtos, vtos, invokestatic        , f1_byte);

生成函數為invokestatic(),生成的匯編代碼如下:

0x00007fffe101c030: mov    %r13,-0x38(%rbp)
0x00007fffe101c034: movzwl 0x1(%r13),%edx
0x00007fffe101c039: mov    -0x28(%rbp),%rcx
0x00007fffe101c03d: shl    $0x2,%edx
0x00007fffe101c040: mov    0x10(%rcx,%rdx,8),%ebx
0x00007fffe101c044: shr    $0x10,%ebx
0x00007fffe101c047: and    $0xff,%ebx
0x00007fffe101c04d: cmp    $0xb8,%ebx
// 檢查invokestatic=184的bytecode是否已經連接,如果已經連接就進行跳轉 
0x00007fffe101c053: je     0x00007fffe101c0f2


// 調用InterpreterRuntime::resolve_invoke()函數對invokestatic=184的
// 的bytecode進行連接,因為字節碼指令還沒有連接
// ... 省略了解析invokestatic的匯編代碼 

// 將invokestatic x中的x加載到%edx中
0x00007fffe101c0e6: movzwl 0x1(%r13),%edx
// 將ConstantPoolCache的首地址存儲到%rcx中
0x00007fffe101c0eb: mov    -0x28(%rbp),%rcx
// %edx中存儲的是ConstantPoolCacheEntry項的索引,轉換為字偏移
0x00007fffe101c0ef: shl    $0x2,%edx


// 獲取ConstantPoolCache::_f1屬性的值
0x00007fffe101c0f2: mov    0x18(%rcx,%rdx,8),%rbx
// 獲取ConstantPoolCache::_flags屬性的值
0x00007fffe101c0f7: mov    0x28(%rcx,%rdx,8),%edx


// 從flags中獲取return type,也就是從_flags的高4位保存的TosState
0x00007fffe101c0fb: shr    $0x1c,%edx
// 將TemplateInterpreter::invoke_return_entry地址存儲到%r10
0x00007fffe101c0fe: movabs $0x7ffff73b5d00,%r10
// 找到對應return type的invoke_return_entry的地址
0x00007fffe101c108: mov    (%r10,%rdx,8),%rdx
// 通過invokespecial指令調用函數后的返回地址
0x00007fffe101c10c: push   %rdx


// 設置調用者棧頂
0x00007fffe101c10d: lea    0x8(%rsp),%r13
// 向棧中last_sp的位置保存調用者棧頂
0x00007fffe101c112: mov    %r13,-0x10(%rbp)

// 跳轉到Method::_from_interpretered_entry入口去執行
0x00007fffe101c116: jmpq   *0x58(%rbx)  

invokespecial指令在調用靜態方法時,不需要動態分發。在這個字節碼指令解析完成后,ConstantPoolCacheEntry中的_f1指向目標方法的Method實例,_f2沒有使用,所以如上匯編的邏輯非常簡單,這里不再過多介紹。 

關於invokestatic與invokespecial的解析過程這里就不再過多介紹了,有興趣的可從LinkResolver::resolve_invoke()函數查看具體的解析過程。 

推薦閱讀:

第1篇-關於JVM運行時,開篇說的簡單些

第2篇-JVM虛擬機這樣來調用Java主類的main()方法

第3篇-CallStub新棧幀的創建

第4篇-JVM終於開始調用Java主類的main()方法啦

第5篇-調用Java方法后彈出棧幀及處理返回結果

第6篇-Java方法新棧幀的創建

第7篇-為Java方法創建棧幀

第8篇-dispatch_next()函數分派字節碼

第9篇-字節碼指令的定義

第10篇-初始化模板表

第11篇-認識Stub與StubQueue

第12篇-認識CodeletMark

第13篇-通過InterpreterCodelet存儲機器指令片段

第14篇-生成重要的例程

第15章-解釋器及解釋器生成器

第16章-虛擬機中的匯編器

第17章-x86-64寄存器

第18章-x86指令集之常用指令

第19篇-加載與存儲指令(1)

第20篇-加載與存儲指令之ldc與_fast_aldc指令(2)

第21篇-加載與存儲指令之iload、_fast_iload等(3)

第22篇-虛擬機字節碼之運算指令

第23篇-虛擬機字節碼指令之類型轉換

第24篇-虛擬機對象操作指令之getstatic

第25篇-虛擬機對象操作指令之getfield

第26篇-虛擬機對象操作指令之putstatic

第27篇-虛擬機字節碼指令之操作數棧管理指令

第28篇-虛擬機字節碼指令之控制轉移指令

第29篇-調用Java主類的main()方法

第30篇-main()方法的執行

第31篇-方法調用指令之invokevirtual

第32篇-解析interfacevirtual字節碼指令

第33篇-方法調用指令之invokeinterface

第34篇-解析invokeinterface字節碼指令

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM