以太坊虚拟机按位运算指令
EVM定义了8条按位运算指令,见下表:
下面是按位运算指令的操作码分布图:
AND、OR、XOR、NOT
AND、OR、XOR指令从栈顶弹出两个元素,进行按位运算,然后把结果推入栈顶。以AND指令为例,下面是它的操作示意图:
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指令操作示意图:
读者可以通过下面的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为例,下面是它的操作示意图:
SHR和SAR的区别在于,前者执行逻辑右移(空缺补0),后者执行算术右移(空缺补符号位)。下表总结了这三条位移指令对于操作数的解释,以及计算结果(这里^
表示指数运算):
Solidity语言提供了<<
和>>
运算符,下表总结了这两个运算符的含义(这里**
表示指数运算):
在Constantinople之前,位移运算符使用EXP、MUL、DIV、SDIV等指令实现;从Constantinople开始,位移运算符可以使用位移指令实现。不过请读者注意,<<运算符可以直接编译成SHL指令,但是由于取整方式不同,所以>>运算符并不能直接编译成SAR指令,详见EIP-145和Solidity文档。读者可以通过下面的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比较操作指令。
原文地址:https://www.cnblogs.com/405845829qq/p/9998251.html