手写JVM(八)-指令集(二)

代码、内容参考来自于张秀宏大佬的自己动手写Java虚拟机 (Java核心技术系列)以及尚硅谷宋红康:JVM全套教程。

1.存储指令

出栈装入局部变量表指令用于将操作数栈中栈顶元素弹出后,装入局部变量表的指定位置,用于给局部变量赋值。

这类指令主要以xstore的形式存在,比如xstore (x为i、l、f、d、 a)、 xstore_n (x为i、l、f、d、 a, n为0 至 3)。

  • 其中,指令istore_n将从操作数栈中弹出一个整数,并把它赋值给局部变量索引n位置。
  • 指令xstore由于没有隐含参数信息,故需要提供一个byte类型的参数类指定目标局部变量表的位置。

说明:

  • 一般说来,类似像store这样的命令需要带一个参数,用来指明将弹出的元素放在局部变量表的第几个位置。但是,为了尽可能压缩指令大小,使用专门的istore_1指令表示将弹出的元素放置在局部变量表第1个位置。类似的还有istore_0、 istore_2、 istore_3,它们分别表示从操作数栈顶弹出一个元素,存放在局部变量表第0、2、3个位置。
  • 由于局部变量表前几个位置总是非常常用,因此这种做法虽然增加了指令数量,但是可以大大压缩生成的字节码的体积,如果局部变量表很大,需要存储的槽位大于3,那么可以使用istore指令,外加一个参数,用来表示需要存放的槽位位置。

和加载指令刚好相反,存储指令把变量从操作数栈顶弹出,然后存入局部变量表。和加载指令一样,存储指令也可以分为6类。

整体代码

在instructions\stores目录下创建lstore.go文件,在其中定义5条指令,代码如下:

package stores

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)

// Store long into local variable

type LSTORE struct{ base.Index8Instruction }

func (self *LSTORE) Execute(frame *rtda.Frame) {
    _lstore(frame, uint(self.Index))
}

type LSTORE_0 struct{ base.NoOperandsInstruction }

func (self *LSTORE_0) Execute(frame *rtda.Frame) {
    _lstore(frame, 0)
}

type LSTORE_1 struct{ base.NoOperandsInstruction }

func (self *LSTORE_1) Execute(frame *rtda.Frame) {
    _lstore(frame, 1)
}

type LSTORE_2 struct{ base.NoOperandsInstruction }

func (self *LSTORE_2) Execute(frame *rtda.Frame) {
    _lstore(frame, 2)
}

type LSTORE_3 struct{ base.NoOperandsInstruction }

func (self *LSTORE_3) Execute(frame *rtda.Frame) {
    _lstore(frame, 3)
}

func _lstore(frame *rtda.Frame, index uint) {
    val := frame.OperandStack().PopLong()
    frame.LocalVars().SetLong(index, val)
}

在instructions\stores目录下创建astore.go文件,代码如下:

package stores

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)


// Store reference into local variable

type ASTORE struct{ base.Index8Instruction }

func (self *ASTORE) Execute(frame *rtda.Frame) {
    _astore(frame, uint(self.Index))
}

type ASTORE_0 struct{ base.NoOperandsInstruction }

func (self *ASTORE_0) Execute(frame *rtda.Frame) {
    _astore(frame, 0)
}

type ASTORE_1 struct{ base.NoOperandsInstruction }

func (self *ASTORE_1) Execute(frame *rtda.Frame) {
    _astore(frame, 1)
}

type ASTORE_2 struct{ base.NoOperandsInstruction }

func (self *ASTORE_2) Execute(frame *rtda.Frame) {
    _astore(frame, 2)
}

type ASTORE_3 struct{ base.NoOperandsInstruction }

func (self *ASTORE_3) Execute(frame *rtda.Frame) {
    _astore(frame, 3)
}

func _astore(frame *rtda.Frame, index uint) {
    ref := frame.OperandStack().PopRef()
    frame.LocalVars().SetRef(index, ref)
}

 

在instructions\stores目录下创建dstore.go文件,代码如下:

package stores

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)

// Store double into local variable

type DSTORE struct{ base.Index8Instruction }

func (self *DSTORE) Execute(frame *rtda.Frame) {
    _dstore(frame, uint(self.Index))
}

type DSTORE_0 struct{ base.NoOperandsInstruction }

func (self *DSTORE_0) Execute(frame *rtda.Frame) {
    _dstore(frame, 0)
}

type DSTORE_1 struct{ base.NoOperandsInstruction }

func (self *DSTORE_1) Execute(frame *rtda.Frame) {
    _dstore(frame, 1)
}

type DSTORE_2 struct{ base.NoOperandsInstruction }

func (self *DSTORE_2) Execute(frame *rtda.Frame) {
    _dstore(frame, 2)
}

type DSTORE_3 struct{ base.NoOperandsInstruction }

func (self *DSTORE_3) Execute(frame *rtda.Frame) {
    _dstore(frame, 3)
}

func _dstore(frame *rtda.Frame, index uint) {
    val := frame.OperandStack().PopDouble()
    frame.LocalVars().SetDouble(index, val)
}

 

在instructions\stores目录下创建fstore.go文件,代码如下:

package stores

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)


// Store float into local variable

type FSTORE struct{ base.Index8Instruction }

func (self *FSTORE) Execute(frame *rtda.Frame) {
    _fstore(frame, uint(self.Index))
}

type FSTORE_0 struct{ base.NoOperandsInstruction }

func (self *FSTORE_0) Execute(frame *rtda.Frame) {
    _fstore(frame, 0)
}

type FSTORE_1 struct{ base.NoOperandsInstruction }

func (self *FSTORE_1) Execute(frame *rtda.Frame) {
    _fstore(frame, 1)
}

type FSTORE_2 struct{ base.NoOperandsInstruction }

func (self *FSTORE_2) Execute(frame *rtda.Frame) {
    _fstore(frame, 2)
}

type FSTORE_3 struct{ base.NoOperandsInstruction }

func (self *FSTORE_3) Execute(frame *rtda.Frame) {
    _fstore(frame, 3)
}

func _fstore(frame *rtda.Frame, index uint) {
    val := frame.OperandStack().PopFloat()
    frame.LocalVars().SetFloat(index, val)
}

 

在instructions\stores目录下创建istore.go文件,代码如下:

package stores

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)


// Store int into local variable

type ISTORE struct{ base.Index8Instruction }

func (self *ISTORE) Execute(frame *rtda.Frame) {
    _istore(frame, uint(self.Index))
}

type ISTORE_0 struct{ base.NoOperandsInstruction }

func (self *ISTORE_0) Execute(frame *rtda.Frame) {
    _istore(frame, 0)
}

type ISTORE_1 struct{ base.NoOperandsInstruction }

func (self *ISTORE_1) Execute(frame *rtda.Frame) {
    _istore(frame, 1)
}

type ISTORE_2 struct{ base.NoOperandsInstruction }

func (self *ISTORE_2) Execute(frame *rtda.Frame) {
    _istore(frame, 2)
}

type ISTORE_3 struct{ base.NoOperandsInstruction }

func (self *ISTORE_3) Execute(frame *rtda.Frame) {
    _istore(frame, 3)
}

func _istore(frame *rtda.Frame, index uint) {
    val := frame.OperandStack().PopInt()
    frame.LocalVars().SetInt(index, val)
}

 

代码解释

以lstore系列指令为例进行介绍。

同样定义一个函数供5条指令使用,代码如下:

 

lstore指令的索引来自操作数,其Execute()方法如下:

 

其余4条指令的索引隐含在操作码中,以lstore_2为例,其

 

2.栈指令

操作数栈管理指令

如同操作一个普通数据结构中的堆栈那样,JVM提供的操作数栈管理指令,可以用于直接操作操作数栈的指令。

这类指令包括如下内容:

  • 将一个或两个元素从栈顶弹出,并且直接废弃: pop, pop2;
  • 复制栈顶一个或两个数值并将复制值或双份的复制值重新压入栈顶: dup,dup2,dup_x1,dup2_x1,dup_x2,dup2_x2;
  • 将栈最顶端的两个Slot数值位置交换: swap,Java虚拟机没有提供交换两个64位数据类型(long、 double)数值的指令。
  • 指令nop,是一个非常特殊的指令,它的字节码为0x00,和汇编语言中的nop一样,它表示什么都不做,这条指令一般可用于调试、占位等。

这些指令属于通用型,对栈的压入或者弹出无需指明数据类型。

说明:

1.不带_x的指令是复制栈顶数据并压入栈顶。包括两个指令, dup和dup2。dup的系数代表要复制的Slot个数。

  • dup开头的指令用于复制1个Slot(4个字节)的数据。例如1个int或1个reference类型数据
  • dup2开头的指令用于复制2个Slot的数据。例如1个long,或2个int,或1个int+1个float类型

 

2.带_x的指令是复制栈顶数据并插入栈顶以下的某个位置。共有4个指令,dup_x1, dup2_x1,dup_x2, dup2_x2。对于带_x的复制插入指令,只要将指令的dup和x的系数相加,结果即为需要插入的位置。因此

  • dup_x1插入位置:1+1=2,即栈顶2个Slot下面
  • dup_x2插入位置: 1+2=3,即栈顶3个Slot下面
  • dup2_x1插入位置: 2+1=3,即找顶3个Slot下面
  • dup2_x2插入位置: 2+2=4,即栈顶4个Slot下面

 

3.pop:将栈顶的1个Slot数值出栈。例如1个short类型数值。pop2:将栈项的2个slot数值出栈。例如1个double类型数值,或者2个int类型数值。

栈指令直接对操作数栈进行操作,

共9条:

  • pop和pop2指令将栈顶变量弹出,
  • dup系列指令复制栈顶变量,
  • swap指令交换栈顶的两个变量。

和其他类型的指令不同,栈指令并不关心变量类型。为了实现栈指令,需要给OperandStack结构体添加两个方法。

打开rtda\operand_stack.go文件,在其中加入PushSlot()和PopSlot()方法,代码如下:

func (self *OperandStack) PushSlot(slot Slot) {
    self.slots[self.size] = slot
    self.size++
}
func (self *OperandStack) PopSlot() Slot {
    self.size--
    return self.slots[self.size]
}

 

pop和pop2指令

整体代码

在instructions\stack目录下创建pop.go文件,在其中定义pop和pop2指令,代码如下:

package stack

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)

// Pop the top operand stack value

type POP struct{ base.NoOperandsInstruction }

/*
bottom -> top
[...][c][b][a]
            |
            V
[...][c][b]
*/

func (self *POP) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    stack.PopSlot()
}

// Pop the top one or two operand stack values

type POP2 struct{ base.NoOperandsInstruction }


/*
bottom -> top
[...][c][b][a]
         | |
         V V
[...][c]
*/

func (self *POP2) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    stack.PopSlot()
    stack.PopSlot()
}

 

代码解释

pop指令把栈顶变量弹出

 

pop指令只能用于弹出int、float等占用一个操作数栈位置的变量。double和long变量在操作数栈中占据两个位置,需要使用pop2指令弹出

 

dup指令

整体代码

在instructions\stack目录下创建dup.go文件,在其中定义6条指令,代码如下:

package stack

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)

// Duplicate the top operand stack value

type DUP struct{ base.NoOperandsInstruction }

/*
bottom -> top
[...][c][b][a]
             \_
               |
               V
[...][c][b][a][a]
*/

func (self *DUP) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot := stack.PopSlot()
    stack.PushSlot(slot)
    stack.PushSlot(slot)
}


// Duplicate the top operand stack value and insert two values down

type DUP_X1 struct{ base.NoOperandsInstruction }

/*
bottom -> top
[...][c][b][a]
          __/
         |
         V
[...][c][a][b][a]
*/

func (self *DUP_X1) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    stack.PushSlot(slot1)
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
}


// Duplicate the top operand stack value and insert two or three values down

type DUP_X2 struct{ base.NoOperandsInstruction }

/*
bottom -> top
[...][c][b][a]
       _____/
      |
      V
[...][a][c][b][a]
*/

func (self *DUP_X2) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    slot3 := stack.PopSlot()
    stack.PushSlot(slot1)
    stack.PushSlot(slot3)
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
}

// Duplicate the top one or two operand stack values

type DUP2 struct{ base.NoOperandsInstruction }


/*
bottom -> top
[...][c][b][a]____
          \____   |
               |  |
               V  V
[...][c][b][a][b][a]
*/

func (self *DUP2) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
}


// Duplicate the top one or two operand stack values and insert two or three values down

type DUP2_X1 struct{ base.NoOperandsInstruction }


/*
bottom -> top
[...][c][b][a]
       _/ __/
      | |
      V V
[...][b][a][c][b][a]
*/

func (self *DUP2_X1) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    slot3 := stack.PopSlot()
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
    stack.PushSlot(slot3)
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
}


// Duplicate the top one or two operand stack values and insert two, three, or four values down

type DUP2_X2 struct{ base.NoOperandsInstruction }


/*
bottom -> top
[...][d][c][b][a]
       ____/ __/
      |   __/
      V  V
[...][b][a][d][c][b][a]
*/

func (self *DUP2_X2) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    slot3 := stack.PopSlot()
    slot4 := stack.PopSlot()
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
    stack.PushSlot(slot4)
    stack.PushSlot(slot3)
    stack.PushSlot(slot2)
    stack.PushSlot(slot1)
}

 

代码解释

dup指令复制栈顶的单个变量。

 

dup_x1指令复制栈顶的单个变量并插入栈顶2个Slot下面。

其他几条指令大致差不多,不在阐述。

 

swap指令

整体代码

在instructions\stack目录下创建swap.go文件,在其中定义swap指令,代码如下:

package stack

import (
    "jvmgo/ch05/instructions/base"
    "jvmgo/ch05/rtda"
)


// Swap the top two operand stack values

type SWAP struct{ base.NoOperandsInstruction }

/*
bottom -> top
[...][c][b][a]
          \/
          /\
         V  V
[...][c][a][b]
*/

func (self *SWAP) Execute(frame *rtda.Frame) {
    stack := frame.OperandStack()
    slot1 := stack.PopSlot()
    slot2 := stack.PopSlot()
    stack.PushSlot(slot1)
    stack.PushSlot(slot2)
}

 

 

代码解释:

swap指令交换栈顶的两个变量,Execute()方法如下:

代码太简单,不在阐述。

 

3.参考

尚硅谷宋红康:JVM全套教程:https://www.bilibili.com/video/BV1PJ411n7xZ

周志明:深入理解java虚拟机

张秀宏:自己动手写Java虚拟机 (Java核心技术系列)

GO语言官网:Standard library – Go Packages

Java虚拟机规范:Chapter 4. The class File Format (oracle.com)

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇