以太坊虛擬機算術運算指令
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(%表示取模運算,^表示指數運算)。
下面是算術運算指令的操作碼分佈圖:
ADD、MUL、SUB、DIV、SDIV、MOD、SMOD、EXP
這8條指令操作方式比較類似,從棧頂彈出兩個元素,進行計算,然後把計算結果推入棧頂。由於採用二的補碼錶示整數時,加法、減法和乘法運算不用考慮符號位,所以加法、減法和乘法運算都只有一條指令。整除和取模運算需要考慮符號位,所以各有兩條指令。指數運算只操作無符號整數。以ADD指令爲例,下面是它的操作示意圖:
ADDMOD和MULMOD
MULMOD指令依次從棧頂彈出x、y、z三個數,先計算x和y的乘積(不受溢出限制),再計算乘積和z的模,最後把結果推入棧頂。假定乘積不會溢出,那麼MULMOD(x, y, z)
等價於x * y % z
,下面是MULMOD指令的操作示意圖:
ADDMOD指令和MULMOD指令類似,只不過把乘法換成了加法。下面是ADDMOD指令的操作示意圖:
SIGNEXTEND
SIGNEXTEND指令從棧頂依次彈出k和x,並把x解釋爲k+1(0 <= k <= 31)字節有符號整數,然後把x符號擴展至32字節。比如x是二進制10000000,k是0,則符號擴展之後,結果爲二進制1111…10000000(共249個1)。下面是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:虛擬機、編譯器、標準庫》。