• 第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源码剖析系列文章!

      

  • 相关阅读:
    全方位深度剖析--性能测试之LoardRunner 介绍
    国外性能测试博客
    由我主讲的软件测试系列视频之性能测试系列视频讲座目录出炉了
    性能测试之系统监控工具nmon
    性能测试学习内容指南
    性能测试之操作系统计数器分析方法
    JAVA正则表达式:Pattern类与Matcher类详解
    (总结)密码破解之王:Ophcrack彩虹表(Rainbow Tables)原理详解(附:120G彩虹表下载)
    border-collapse实现表格细线边框
    安卓造成内存泄露的几个原因
  • 原文地址:https://www.cnblogs.com/mazhimazhi/p/15266288.html
Copyright © 2020-2023  润新知