zoukankan      html  css  js  c++  java
  • java虚拟机规范(se8)——java虚拟机的编译(二)

    3.3 算术运算

      java虚拟机通常在操作数栈上进行算术运算(例外情况是iinc指令,它直接增加一个局部变量的值)。例如下面的align2grain()方法,它的作用是将int值对齐到2的指定次幂:

    int align2grain(int i, int grain) {
        return ((i + grain-1) & ~(grain-1));
    }

      算术运算的操作数是从操作数栈中弹出的,运算结果会压回操作数栈。因此,算术子计算的结果可以作为嵌套计算的操作数。例如。~(grain-1)的计算结果就是被这样使用的:

    5   iload_2        // Push grain
    6   iconst_1       // Push int constant 1
    7   isub           // Subtract; push result
    8   iconst_m1      // Push int constant -1
    9   ixor           // Do XOR; push result

      首先使用局部变量2的值和一个int类型的立即数1来计算grain-1的值。这些操作数被操作数栈弹出,然后他们的差值压回操作数栈,这个差值可以立即作为一个操作数被ixor指令使用(回想,~x == -1 ^ x)。同样的,ixor指令的计算结果成为接下来iand指令的一个操作数。

      这个方法的代码如下:

    Method int align2grain(int,int)
    0   iload_1
    1   iload_2
    2   iadd
    3   iconst_1
    4   isub
    5   iload_2
    6   iconst_1
    7   isub
    8   iconst_m1
    9   ixor
    10  iand
    11  ireturn

    3.4 访问运行时常量池

      许多数值类型常量,以及对象,字段和方法是通过当前类的运行时常量池来访问的。对象的访问在3.8节讨论。数据类型为int,long,float和double以及String类示例的引用通过ldc,ldc_w和ldc2_w来管理。

      ldc和ldc_w指令用于访问运行时常量池中除了double和long类型的值(但是包括String类型实例)。当使用的运行时常量池的项目过多时(多余256个,一个字节能表示的范围),需要使用ldc_w来代替。ldc2_w用于访问所有double和long类型的值,它没有对应的非宽版本,即没有ldc2指令。

      整型常量类型byte,char,short,以及小的int值,可能使用bipush,sipush或者iconst_<i>指令来编译。某些小的浮点型常量也可以使用fconst_<f>和dconst_<d>指令来编译。

      在所有这些情况下,编译都是非常直观的,例如,下面这些常量:

    void useManyNumeric() {
        int i = 100;
        int j = 1000000;
        long l1 = 1;
        long l2 = 0xffffffff;
        double d = 2.2;
        ...do some calculations...
    }

      编译后:

    Method void useManyNumeric()
    0   bipush 100   // Push small int constant with bipush
    2   istore_1
    3   ldc #1       // Push large int constant (1000000) with ldc
    5   istore_2
    6   lconst_1     // A tiny long value uses small fast lconst_1
    7   lstore_3
    8   ldc2_w #6    // Push long 0xffffffff (that is, an int -1)
            // Any long constant value can be pushed with ldc2_w
    11  lstore 5
    13  ldc2_w #8    // Push double constant 2.200000
            // Uncommon double values are also pushed with ldc2_w
    16  dstore 7
    ...do those calculations...

    3.5 更多控制结构的示例

      for语句的编译已经在前面的3.2节中展示过了。绝大多数java编程语言的其他控制结构(if-then-else,do,while,break和continue)也同样使用明细的方式来编译。switch语句的编译在单独的章节(3.10)介绍,异常的编译(3.12),finally从句的编译(3.13)。

      作为进一步的示例,while循环以一种明显的方式编译,尽管Java虚拟机提供的特定控制传输指令因数据类型的不同而不同。像往常一样,对int类型的数据有更多的支持,例如:

    void whileInt() {
        int i = 0;
        while (i < 100) {
            i++;
        }
    }

      编译为:

    Method void whileInt()
    0   iconst_0
    1   istore_1
    2   goto 8
    5   iinc 1 1
    8   iload_1
    9   bipush 100
    11  if_icmplt 5
    14  return

      注意,while语句的条件判断(使用if_icmplt指令实现)位于Java虚拟机代码循环的的底部。(在前面的spin例子中也是如此)。位于循环底部的条件判断强制使用goto指令,以便在循环的第一次迭代之前进行条件判断。如果不满足条件,并且循环体从未进入,那么这个额外的指令就被浪费了。不过while循环通常用于期望循环体会执行的场景中。对于后续的迭代,将判断条件放在循环的底部每次循环时都会节省一条Java虚拟机指令:如果判断条件位于循环的顶部,则循环体将需要一条尾随的goto指令才能回到顶部。

      使用其他数据类型的控制结构都使用相同的方式来编译,但是必须使用对应数据类型的指令。这会导致一些效率不高的代码,因为需要更多的java虚拟机指令,例如:

    void whileDouble() {
        double i = 0.0;
        while (i < 100.1) {
            i++;
        }
    }

      编译成:

    Method void whileDouble()
    0   dconst_0
    1   dstore_1
    2   goto 9
    5   dload_1
    6   dconst_1
    7   dadd
    8   dstore_1
    9   dload_1
    10  ldc2_w #4      // Push double constant 100.1
    13  dcmpg          // To compare and branch we have to use...
    14  iflt 5         // ...two instructions
    17  return

      每一种浮点类型有两个比较指令:float的fcmpl和fcmpg,double的dcmpl和dcmpg。这些指令语义相似,仅仅在对待NaN变量时有所区别。NaN是无序的,所以只要有一个操作数是NaN,浮点指令的比较结果都会失败。无论比较操作是否会因为遇到NaN值而失败,编译器都会根据不用的操作类型来选择不同的比较指令。

  • 相关阅读:
    【tarjan】【树的直径】【CF】K. Königsberg Bridges
    【组合数学】【恒等式】简单和、交错和
    【组合数学】【恒等式】$sum_{k=0}^{r}C_m^k imes C_{n}^{r-k}=C_{m+n}^r$
    【组合数学】【恒等式】$C_{n}^{r} imes C_{n-r}^{k-r}=C_{n}^{k} imes C_k^{r}$
    【树形DP】D. Serval and Rooted Tree
    【图论】图的欧拉定理
    【图论】网络流解决二分图最大匹配量问题
    【计算几何】atan2函数
    【单峰计数DP】Problem F – Fabricating Sculptures
    Java基础之:自定义泛型
  • 原文地址:https://www.cnblogs.com/lilinwei340/p/10948570.html
Copyright © 2011-2022 走看看