翻译Lua 5.1 VM Instructions(三)

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

6 Loading Constants


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

装载和移动几乎是所有的处理器和虚拟机指令集的起点,所以我们将以原始的装载和移动开始:

rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

MOVE               A B             R(A) := R(B)

 

将寄存器R(B)中的内容拷贝到寄存器R(A)中。如果R(B)保存着表,函数或用户数据,那么对象的引用将会被。MOVE经常被用来向下一条指令要使用的空间中转移数据。

 

MOVE进行编码还有第二个目的 它也被用在创建闭包当中,通常出现在CLOSURE指令后面;查看CLOSURE获得更多信息。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

MOVE最直接的使用是将一个局部变量赋值给另一个局部变量。

>local a,b = 10; b = a

; function [0] definition (level 1)

; 0 upvalues, 0 params, 2 stacks

.function 0 0 2 2

.local "a" ; 0

.local "b" ; 1

.const 10 ; 0

[1] loadk 0 0 ; 10

[2] loadnil 1 1

[3] move 1 0

[4] return 0 1

; end of function

 

[3]将局部变量a (寄存器0)中的值赋值(拷贝)给局部变量b (寄存器1)。你不会看到MOVE用在算术表达式中,因为算术操作不需要它。所有的算术操作都具有2个或3个操作数的形式;对于操作数R(A),R(B)R(C)来说全部的局部堆栈都是可以访问的,所以不需要额外的MOVE指令。

 

你会看到MOVE指令的其他地方:

为了函数调用将参数放入特定空间时

为了某些指令将值放入特定的空间时,堆栈的顺序对于这些指令来说很重要,例如GETTABLESETTABLECONCAT

当函数调用结束后把返回结果拷贝到局部变量中。

CLOSURE指令后面(在第14章讨论)

 

3个基本的指令用于将常量装载到局部变量中。其他的用于读写管局变量,upvalues和表的指令在接下来的章节讨论。第一个常量装载指令是LOADNIL


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADNIL         A B      R(A) := ... := R(B) := nil

 

将从R(A)R(B)这个范围内的寄存器设置成nil。如果要设置一个寄存器,使R(A) = R(B)。当两个或更多的寄存器需要设置成nil值,仅需要一条 LOADNIL指令。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADNIL使用操作数AB作为寄存器位置的范围。上一页中的MOVE的例子中展示了LOADNIL被用来将一个寄存器设置成nil

 

>local a,b,c,d,e = nil,nil,0

; function [0] definition (level 1)

; 0 upvalues, 0 params, 5 stacks

.function 0 0 2 5

.local "a" ; 0

.local "b" ; 1

.local "c" ; 2

.local "d" ; 3

.local "e" ; 4

.const 0 ; 0

[1] loadk 2 0 ; 0

[2] loadnil 3 4

[3] return 0 1

; end of function

 

[2] nils位于de。局部变量ab不需要LOADNIL,因为指令早已被优化过了。局部变量c被显示的初始化为0。对于局部变量abLOADNIL总是被优化,因为在执行函数之前Lua虚拟机总是设置将所有的局部变量设置成nil优化规则是很简单的;如果没有其他指令产生,作为第一条指令的LOADNIL总是被优化掉。

 

在例子中虽然行[2]LOADNIL是多余的,但是它仍然作为不在行[1]LOADNIL指令生成了。理想的情况下,在做任何事情之前,应该将所有的要初始化为nil的局部变量都放在函数的顶端。像上面所述的情形,我们应该重新排列局部变量以获得优化规则带来的好处:

 

>local a,b,d,e local c=0

; function [0] definition (level 1)

; 0 upvalues, 0 params, 5 stacks

.function 0 0 2 5

.local "a" ; 0

.local "b" ; 1

.local "d" ; 2

.local "e" ; 3

.local "c" ; 4

.const 0 ; 0

[1] loadk 4 0 ; 0

[2] return 0 1

; end of function

 

现在,我们节省了一条LOADNIL指令。在函数的其他部分,一个将nil给局部变量的赋值将理所当然的要求一条LOADNIL指令。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADK             A   Bx               R(A) := Kst(Bx)

 

将计数为Bx的常量装载到寄存器R(A)。常量通常是数字或字符串。每一个函数都有自己的常量列表或池。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADK将常量列表中的常量装载到寄存器或局部变量中。常量从0开始索引。许多指令,如算术指令,可以不需要LOADK就能使用常量列表。常量集中在列表中,重复的被删除。这个列表可以保存nils,布尔值,数值或字符串。

 

>local a,b,c,d = 3,"foo",3,"foo"

; function [0] definition (level 1)

; 0 upvalues, 0 params, 4 stacks

.function 0 0 2 4

.local "a" ; 0

.local "b" ; 1

.local "c" ; 2

.local "d" ; 3

.const 3 ; 0

.const "foo" ; 1

[1] loadk 0 0 ; 3

[2] loadk 1 1 ; "foo"

[3] loadk 2 0 ; 3

[4] loadk 3 1 ; "foo"

[5] return 0 1

; end of function

 

常量3和常量字符串“foo”在源代码片段中都出现了两次,但是在常量列表中,每一个常量只有单独一个位置。常量列表也保存着全局变量的名字,GETGLOBALSETGLOBAL隐含的执行了LOADK操作,为了在全局表中查寻全局变量之前得到全局变量的名字。

 

最后一个常量装载指令是LOADBOOL,则个指令是为了设置布尔值,同时它还有一些其他的功能。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADBOOL           A B C         R(A) := (Bool)B; if (C) PC++

 

将布尔值(true or false)装载到寄存器R(A)true通常被编码成整数1false通常是0。如果C是非0值,那么下一条指令将会被跳过(当一条赋值语句中的表达式是关系操作的时候就会用到它。例如M = K>5.)

在域B中你可以使用任何非0值作为布尔值true,但是在Lua中你不能把布尔值当作数字来用,最好将1true联系在一起。


rel="File-List" href="file:///C:%5CDOCUME%7E1%5CWangBo%5CLOCALS%7E1%5CTemp%5Cmsohtml1%5C01%5Cclip_filelist.xml">

LOADBOOL被用来向寄存器中装载一个布尔值。也被用在希望产生布尔结果的地方,因为条件测试指令不产生布尔结果 它可以代替条件跳转。为了实现跳转代码操作数C被用来跳过下条指令(通过将PC1)。在简单的布尔赋值中,C总是0

 

>local a,b = true,false

; function [0] definition (level 1)

; 0 upvalues, 0 params, 2 stacks

.function 0 0 2 2

.local "a" ; 0

.local "b" ; 1

[1] loadbool 0 1 0 ; true

[2] loadbool 1 0 0 ; false

[3] return 0 1

; end of function

 

这个例子很明显:行[1]true赋值给局部变量a (寄存器0)[2]false赋值给局部变量b (寄存器1)。在上面的两种情况中,C都是0,所以PC没有增加,下一条指令没有被跳过。

 

>local a = 5 > 2

; function [0] definition (level 1)

; 0 upvalues, 0 params, 2 stacks

.function 0 0 2 2

.local "a" ; 0

.const 5 ; 0

.const 2 ; 1

[1] lt 1 257 256 ; 2 5, to [3] if false

[2] jmp 1 ; to [4]

[3] loadbool 0 0 1 ; false, to [5]

[4] loadbool 0 1 0 ; true

[5] return 0 1

; end of function

 

在这个例子中的表达式给出了一个布尔结果并把它赋值给了变量。注意Lua并没有把表达式优化成true值;Lua 5.1并没有实现编译时对关系运算的常量求值,但是它实现了简单的算术运算常量求值。

 

关系操作符LT(后面会更详细的介绍)不能给出一个布尔值但是实现了一个条件跳转,LOADBOOL使用它的C操作数在行[3]处实现了一个无条件跳转 节省了一条指令并且把事情做的更加整洁了。上述这些情况的原因时对于if...then块的指令集做了简单的优化。本质上来说,local a = 5 > 2是一下面的方式执行的:

 

local a

if 2 < 5 then

a = true

else

a = false

end

 

在反汇编列表中,LT测试2 < 5,求值的结果为true,并且不执行条件跳转。行[2]跳过的错误结果的路径,在行[4]局部变量a(寄存器0)通过LOADBOOL指令,被赋值为布尔值true。如果25颠倒一下,那么接下来的应该是行[3],设置false,然后结果为true的路径([4])会被跳过,因为LOADBOOL有一个非0的域C

 

true结果路径会是这样(括号中为附加的注释)

 

[1] lt         1 257 256 ; 2 5, to [3] if false (if 2 < 5)

[2] jmp         1      ; to [4]

[4] loadbool      0 1   ; true (a = true)

[5] return       0 1

 

false结果路径(在本例中它永远不会被执行)会是这样:

 

[1] lt          1 257 256 ; 2 5, to [3] if false (if 2 < 5)

[3] loadbool       0 0  ; false, to [5] (a = false)

[5] return        0 1

 

true结果的路径看起来更长,其实不是这样的,这取决于虚拟机现实的方法。在关系运算和逻辑运算指令的章节将会更深入的讨论。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值