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


Java虛擬機規范中定義的類型轉換相關的字節碼指令如下表所示。

0x85

i2l

將棧頂int型數值強制轉換成long型數值並將結果壓入棧頂

0x86

i2f

將棧頂int型數值強制轉換成float型數值並將結果壓入棧頂

0x87

i2d

將棧頂int型數值強制轉換成double型數值並將結果壓入棧頂

0x88

l2i

將棧頂long型數值強制轉換成int型數值並將結果壓入棧頂

0x89

l2f

將棧頂long型數值強制轉換成float型數值並將結果壓入棧頂

0x8a

l2d

將棧頂long型數值強制轉換成double型數值並將結果壓入棧頂

0x8b

f2i

將棧頂float型數值強制轉換成int型數值並將結果壓入棧頂

0x8c

f2l

將棧頂float型數值強制轉換成long型數值並將結果壓入棧頂

0x8d

f2d

將棧頂float型數值強制轉換成double型數值並將結果壓入棧頂

0x8e

d2i

將棧頂double型數值強制轉換成int型數值並將結果壓入棧頂

0x8f

d2l

將棧頂double型數值強制轉換成long型數值並將結果壓入棧頂

0x90

d2f

將棧頂double型數值強制轉換成float型數值並將結果壓入棧頂

0x91

i2b

將棧頂int型數值強制轉換成byte型數值並將結果壓入棧頂

0x92

i2c

將棧頂int型數值強制轉換成char型數值並將結果壓入棧頂

0x93

i2s

將棧頂int型數值強制轉換成short型數值並將結果壓入棧頂

上表字節碼指令的模板定義如下:

def(Bytecodes::_i2l   , ____|____|____|____, itos, ltos, convert ,  _           );
def(Bytecodes::_i2f   , ____|____|____|____, itos, ftos, convert ,  _           );
def(Bytecodes::_i2d   , ____|____|____|____, itos, dtos, convert ,  _           );
def(Bytecodes::_l2i   , ____|____|____|____, ltos, itos, convert ,  _           );
def(Bytecodes::_l2f   , ____|____|____|____, ltos, ftos, convert ,  _           );
def(Bytecodes::_l2d   , ____|____|____|____, ltos, dtos, convert ,  _           );
def(Bytecodes::_f2i   , ____|____|____|____, ftos, itos, convert ,  _           );
def(Bytecodes::_f2l   , ____|____|____|____, ftos, ltos, convert ,  _           );
def(Bytecodes::_f2d   , ____|____|____|____, ftos, dtos, convert ,  _           );
def(Bytecodes::_d2i   , ____|____|____|____, dtos, itos, convert ,  _           );
def(Bytecodes::_d2l   , ____|____|____|____, dtos, ltos, convert ,  _           );
def(Bytecodes::_d2f   , ____|____|____|____, dtos, ftos, convert ,  _           );
def(Bytecodes::_i2b   , ____|____|____|____, itos, itos, convert ,  _           );
def(Bytecodes::_i2c   , ____|____|____|____, itos, itos, convert ,  _           );
def(Bytecodes::_i2s   , ____|____|____|____, itos, itos, convert ,  _           ); 

相關字節碼轉換指令的生成函數為TemplateTable::convert(),此函數的實現如下:

void TemplateTable::convert() {
  static const int64_t is_nan = 0x8000000000000000L;

  // Conversion
  switch (bytecode()) {
  case Bytecodes::_i2l:
    __ movslq(rax, rax);
    break;
  case Bytecodes::_i2f:
    __ cvtsi2ssl(xmm0, rax);
    break;
  case Bytecodes::_i2d:
    __ cvtsi2sdl(xmm0, rax);
    break;
  case Bytecodes::_i2b:
    __ movsbl(rax, rax);
    break;
  case Bytecodes::_i2c:
    __ movzwl(rax, rax);
    break;
  case Bytecodes::_i2s:
    __ movswl(rax, rax);
    break;
  case Bytecodes::_l2i:
    __ movl(rax, rax);
    break;
  case Bytecodes::_l2f:
    __ cvtsi2ssq(xmm0, rax);
    break;
  case Bytecodes::_l2d:
    __ cvtsi2sdq(xmm0, rax);
    break;
  case Bytecodes::_f2i:
  {
    Label L;
    __ cvttss2sil(rax, xmm0);
    __ cmpl(rax, 0x80000000); // NaN or overflow/underflow?
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2i), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_f2l:
  {
    Label L;
    __ cvttss2siq(rax, xmm0);
    // NaN or overflow/underflow?
    __ cmp64(rax, ExternalAddress((address) &is_nan));
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::f2l), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_f2d:
    __ cvtss2sd(xmm0, xmm0);
    break;
  case Bytecodes::_d2i:
  {
    Label L;
    __ cvttsd2sil(rax, xmm0);
    __ cmpl(rax, 0x80000000); // NaN or overflow/underflow?
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2i), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_d2l:
  {
    Label L;
    __ cvttsd2siq(rax, xmm0);
    // NaN or overflow/underflow?
    __ cmp64(rax, ExternalAddress((address) &is_nan));
    __ jcc(Assembler::notEqual, L);
    __ call_VM_leaf(CAST_FROM_FN_PTR(address, SharedRuntime::d2l), 1);
    __ bind(L);
  }
    break;
  case Bytecodes::_d2f:
    __ cvtsd2ss(xmm0, xmm0);
    break;
  default:
    ShouldNotReachHere();
  }
}

如_i2l指令將棧頂int型數值強制轉換成long型數值並將結果壓入棧頂,其對應的匯編代碼如下:

movslq %eax,%rax  // 將一個雙字擴展后送到一個四字中

對於浮點數float或long轉int或long類型相對復雜,下面看一個float轉int類型的f2i指令。

// 把標量單精度數轉換為占用雙字的標量整數
0x00007fffe1019189: vcvttss2si %xmm0,%eax
// 和0x80000000進行比較,如果不相等,則跳轉到L
0x00007fffe101918d: cmp    $0x80000000,%eax
0x00007fffe1019193: jne    0x00007fffe10191bc

// 如果棧頂指針已經按16字節對齊,則可直接調用調用SharedRuntime::f2i()函數,否則
// 將棧頂指令按16字節對齊后再調用

0x00007fffe1019199: test   $0xf,%esp
0x00007fffe101919f: je     0x00007fffe10191b7
0x00007fffe10191a5: sub    $0x8,%rsp
// 調用SharedRuntime::f2i()函數
0x00007fffe10191a9: callq  0x00007ffff6a0f946
0x00007fffe10191ae: add    $0x8,%rsp
0x00007fffe10191b2: jmpq   0x00007fffe10191bc
// 調用SharedRuntime::f2i()函數
0x00007fffe10191b7: callq  0x00007ffff6a0f946 

---- L ----

生成的匯編指令vcvttss2si的意思為把標量單精度數轉換為占用雙字的標量整數,名稱的由來解讀如下:

cvt:convert,轉換;

t:truncation,截斷;

ss:scalar single,標量單精度數;

2:to;

si:scalar integer,標量整數。

調用的SharedRuntime::f2i()函數的實現如下:

JRT_LEAF(jint, SharedRuntime::f2i(jfloat  x))
  if (g_isnan(x))  // 如果為非數字值,直接返回0
    return 0;
  if (x >= (jfloat) max_jint)
    return max_jint;
  if (x <= (jfloat) min_jint)
    return min_jint;
  return (jint) x;
JRT_END

C++函數調用時,需要一個參數x,在GNU / Linux上遵循System V AMD64 ABI的調用約定。寄存器RDI,RSI,RDX,RCX,R8和R9是用於整數和存儲器地址的參數和XMM0,XMM1,XMM2,XMM3,XMM4,XMM5,XMM6和XMM7用於浮點參數,所以將用xmm0做為第1個參數,這個參數恰好是棧頂用來緩存浮點數的寄存器,所以默認不用任何操作。 

返回值存儲到%rax中,由於tos_out為itos,%rax寄存器用來做棧頂緩存,所以也不需要做額外的操作。

 

推薦閱讀:

第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篇-虛擬機字節碼之運算指令

如果有問題可直接評論留言或加作者微信mazhimazh

關注公眾號,有HotSpot VM源碼剖析系列文章!

 

 

  

 


免責聲明!

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



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