以太坊虚拟机介绍3-算术运算指令

以太坊虚拟机算术运算指令

EVM总共定义了11条算术运算指令,见下表:

算术运算指令 操作码 说明
ADD 0x01 加法运算
MUL 0x02 乘法运算
SUB 0x03 减法运算
DIV 0x04 无符号整除运算
SDIV 0x05 有符号整除运算
MOD 0x06 无符号取模运算
SMOD 0x07 有符号取模运算
ADDMOD 0x08 后面解释
MULMOD 0x09 后面解释
EXP 0x0A 指数运算
SIGNEXTEND 0x0B 符号位扩展

这些指令从栈顶弹出两到三个元素,进行相应计算,然后把结果推入栈顶。参与计算的元素和结果均被解释为按二的补码编码的整数。如果计算结果(假设为x)溢出(超出2^256),则最终的结果x’取值x % 2^256(%表示取模运算,^表示指数运算)。

下面是算术运算指令的操作码分布图:
arith opcodes

ADD、MUL、SUB、DIV、SDIV、MOD、SMOD、EXP

这8条指令操作方式比较类似,从栈顶弹出两个元素,进行计算,然后把计算结果推入栈顶。由于采用二的补码表示整数时,加法、减法和乘法运算不用考虑符号位,所以加法、减法和乘法运算都只有一条指令。整除和取模运算需要考虑符号位,所以各有两条指令。指数运算只操作无符号整数。以ADD指令为例,下面是它的操作示意图:
ADD

ADDMOD和MULMOD

MULMOD指令依次从栈顶弹出x、y、z三个数,先计算x和y的乘积(不受溢出限制),再计算乘积和z的模,最后把结果推入栈顶。假定乘积不会溢出,那么MULMOD(x, y, z)等价于x * y % z,下面是MULMOD指令的操作示意图:
MULMOD

ADDMOD指令和MULMOD指令类似,只不过把乘法换成了加法。下面是ADDMOD指令的操作示意图:
ADDMOD

SIGNEXTEND

SIGNEXTEND指令从栈顶依次弹出k和x,并把x解释为k+1(0 <= k <= 31)字节有符号整数,然后把x符号扩展至32字节。比如x是二进制10000000,k是0,则符号扩展之后,结果为二进制1111…10000000(共249个1)。下面是SIGNEXTEND指令的操作示意图:
SIGNEXTEND

实例分析

ADD、MUL、SUB、DIV、SDIV、MOD、SMOD、EXP指令与Solidity语言里的+*-/%** 运算符直接对应。ADDMOD指令对应addmod()函数,MULMOD指令对应mulmod()函数。暂时还没有搞清楚SIGNEXTEND指令的用法,等以后再补充。下面的Solidity代码演示了EVM算术运算指令的具体应用:

// arith_demo.sol
pragma solidity ^0.4.24;

contract C {

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

        u3 = u1 + u2; // ADD
        u3 = u1 * u2; // MUL
        u3 = u1 - u2; // SUB
        u3 = u1 / u2; // DIV
        s3 = s1 / s2; // SDIV
        u3 = u1 % u2; // MOD
        s3 = s1 % s2; // SMOD
        u3 = u1 ** u2; // EXP
        u3 = addmod(u1, u2, k); // ADDMOD
        u3 = mulmod(u1, u2, k); // MULMOD
    }

}

读者可以运行solc --asm --opcodes arith_demo.sol命令观察编译器生成的字节码。

总结

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

你可能感兴趣的:(EVM)