這一篇將詳細介紹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()函數查看具體的解析過程。
推薦閱讀:
第2篇-JVM虛擬機這樣來調用Java主類的main()方法
第13篇-通過InterpreterCodelet存儲機器指令片段
第20篇-加載與存儲指令之ldc與_fast_aldc指令(2)
第21篇-加載與存儲指令之iload、_fast_iload等(3)