以太坊虚拟机介绍4-按位运算指令

以太坊虚拟机按位运算指令

EVM定义了8条按位运算指令,见下表:

按位运算指令操作码说明
AND0x16按位与
OR0x17按位或
XOR0x18按位异或
NOT0x19按位取反
BYTE0x1A取第n个字节
SHL0x1B左移
SHR0x1C逻辑右移
SAR0x1D算术右移

下面是按位运算指令的操作码分布图:
bitwise opcodes

AND、OR、XOR、NOT

AND、OR、XOR指令从栈顶弹出两个元素,进行按位运算,然后把结果推入栈顶。以AND指令为例,下面是它的操作示意图:
AND

NOT指令将栈元素按位取反,下面是它的操作示意图:
NOT

这四条指令分别与Solidity语言里的&|^~运算符直接对应,下面的Solidity代码演示了这四条指令的具体应用(读者可以运行solc --asm --opcodes bitwise_demo1.sol命令观察编译器生成的字节码):

// bitwise_demo1.sol 
pragma solidity ^0.4.24;

contract C {

    function test() public pure {
        int s1; int s2; int s3;
        uint u1; uint u2; uint u3;

        s3 = s1 & s2; // ADD
        s3 = s1 | s2; // OR
        s3 = s1 ^ s2; // XOR
        s3 = ~ s1;    // NOT

        u3 = u1 & u2; // ADD
        u3 = u1 | u2; // OR
        u3 = u1 ^ u2; // XOR
        u3 = ~ u1;    // NOT
    }

}

BYTE

BYTE指令先后从栈顶弹出n和x,取x的第n个字节并推入栈顶。由于EVM的字长是32个字节,所以n在[0, 31]区间内才有意义,否则BYTE的运算结果就是0。另外,字节是从左到右数的,因此第0个字节占据字的最高位8个比特。以n=1为例,下面是BYTE指令操作示意图:

BYTE

读者可以通过下面的Solidity代码观察BYTE指令的用法:

// byte_demo2.sol 
pragma solidity ^0.4.24;

contract C {

    function test() public pure {
        bytes32 a;
        bytes1 b = a[31]; // ... BYTE ...
    }

}

SHL、SHR、SAR

这三条位移指令是由EIP-145引入的,从Constantinople虚拟机开始支持。这三条指令都是先后从栈顶弹出两个数n和x,其中x是要进行位移操作顶数,n是位移比特数,然后把结果推入栈顶。以左移指令SHL为例,下面是它的操作示意图:
SHL

SHR和SAR的区别在于,前者执行逻辑右移(空缺补0),后者执行算术右移(空缺补符号位)。下表总结了这三条位移指令对于操作数的解释,以及计算结果(这里^表示指数运算):

指令xn结果
SHLunsignedunsigned(arg2 * 2^arg1) mod 2^256
SHRunsignedunsignedfloor(arg2 / 2^arg1)
SARsignedunsignedfloor(arg2 / 2^arg1)

Solidity语言提供了<<>>运算符,下表总结了这两个运算符的含义(这里**表示指数运算):

运算符解释
x >> nx * 2**y
x << nx / 2**y

在Constantinople之前,位移运算符使用EXP、MUL、DIV、SDIV等指令实现;从Constantinople开始,位移运算符可以使用位移指令实现。不过请读者注意,<<运算符可以直接编译成SHL指令,但是由于取整方式不同,所以>>运算符并不能直接编译成SAR指令,详见EIP-145Solidity文档。读者可以通过下面的Solidity代码观察位移指令的用法(可以通过--evm-version选项告诉Solidity编译器目标EVM版本,例如solc --asm --opcodes --evm-version constantinople bitwise_demo2.sol,如不指定,默认是byzantium):

// bitwise_demo2.sol 
pragma solidity ^0.4.24;

contract C {

    function test() public pure {
        int s1; int s2;
        uint u1; uint u2;
        uint n;

        u2 = u1 << n; // SHL
        s2 = s1 << n; // SHL
        u2 = u1 >> n; // SHR
        s2 = s1 >> n; // EXP、SDIV
        //s2 = s1 >>> n; // SHR?
    }

}

总结

本文介绍了EVM按位运算指令,下一篇文章将介绍EVM比较操作指令。如果大家对编程语言虚拟机有更多的兴趣,请关注我写的《自己动手写Java虚拟机》,以及马上将要出版的《自己动手实现Lua:虚拟机、编译器、标准库》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值