Alert Source Discuss
Standards Track: Core

EIP-145: EVM 中的按位移位指令

提供与其它算术运算成本相当的原生按位移位。

Authors Alex Beregszaszi (@axic), Paweł Bylica (@chfast)
Created 2017-02-13

摘要

引入原生按位移位指令,这在主机上的处理效率更高,并且合约使用起来更便宜。

动机

EVM 缺少按位移位运算符,但支持其他逻辑和算术运算符。移位操作可以通过算术运算符来实现,但这具有更高的成本,并且需要主机花费更多的处理时间。使用算术实现 SHLSHR 各自花费 35 gas,而提议的指令消耗 3 gas。

规范

引入以下指令:

0x1b: SHL (左移)

SHL 指令(左移)从堆栈中弹出 2 个值,首先是 arg1,然后是 arg2,并将 arg2 左移 arg1 位数后压入堆栈。结果等于

(arg2 * 2^arg1) mod 2^256

注意:

  • 该值 (arg2) 被解释为无符号数。
  • 移位量 (arg1) 被解释为无符号数。
  • 如果移位量 (arg1) 大于或等于 256,则结果为 0。
  • 这等效于 PUSH1 2 EXP MUL

0x1c: SHR (逻辑右移)

SHR 指令(逻辑右移)从堆栈中弹出 2 个值,首先是 arg1,然后是 arg2,并将 arg2 右移 arg1 位数,并用零填充后压入堆栈。结果等于

floor(arg2 / 2^arg1)

注意:

  • 该值 (arg2) 被解释为无符号数。
  • 移位量 (arg1) 被解释为无符号数。
  • 如果移位量 (arg1) 大于或等于 256,则结果为 0。
  • 这等效于 PUSH1 2 EXP DIV

0x1d: SAR (算术右移)

SAR 指令(算术右移)从堆栈中弹出 2 个值,首先是 arg1,然后是 arg2,并将 arg2 右移 arg1 位数,并进行符号扩展后压入堆栈。结果等于

floor(arg2 / 2^arg1)

注意:

  • 该值 (arg2) 被解释为有符号数。
  • 移位量 (arg1) 被解释为无符号数。
  • 如果移位量 (arg1) 大于或等于 256,则如果 arg2 为非负数,则结果为 0,如果 arg2 为负数,则结果为 -1。
  • 这与 PUSH1 2 EXP SDIV 不等效,因为它采用不同的舍入方式。参见 SDIV(-1, 2) == 0,而 SAR(-1, 1) == -1

移位指令的成本设置为 verylow 等级(3 gas)。

原理

选择指令操作数是为了适应已经位于堆栈上的值的更自然的移位用例。这意味着与大多数算术指令相比,操作数顺序被交换了。

向后兼容性

新引入的指令对过去创建的字节码没有影响。

测试用例

SHL (左移)

  1. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x00
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  2. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x01
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000002
    
  3. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0xff
    SHL
    ---
    0x8000000000000000000000000000000000000000000000000000000000000000
    
  4. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x0100
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  5. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x0101
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  6. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x00
    SHL
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  7. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x01
    SHL
    ---
    0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
    
  8. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xff
    SHL
    ---
    0x8000000000000000000000000000000000000000000000000000000000000000
    
  9. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x0100
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  10. PUSH 0x0000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x01
    SHL
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  11. PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x01
    SHL
    ---
    0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe
    

SHR (逻辑右移)

  1. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x00
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  2. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x01
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  3. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x01
    SHR
    ---
    0x4000000000000000000000000000000000000000000000000000000000000000
    
  4. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0xff
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  5. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x0100
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  6. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x0101
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  7. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x00
    SHR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  8. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x01
    SHR
    ---
    0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  9. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xff
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  10. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x0100
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  11. PUSH 0x0000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x01
    SHR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    

SAR (算术右移)

  1. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x00
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  2. PUSH 0x0000000000000000000000000000000000000000000000000000000000000001
    PUSH 0x01
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  3. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x01
    SAR
    ---
    0xc000000000000000000000000000000000000000000000000000000000000000
    
  4. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0xff
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  5. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x0100
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  6. PUSH 0x8000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x0101
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  7. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x00
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  8. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x01
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  9. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xff
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  10. PUSH 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x0100
    SAR
    ---
    0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    
  11. PUSH 0x0000000000000000000000000000000000000000000000000000000000000000
    PUSH 0x01
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  12. PUSH 0x4000000000000000000000000000000000000000000000000000000000000000
    PUSH 0xfe
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  13. PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xf8
    SAR
    ---
    0x000000000000000000000000000000000000000000000000000000000000007f
    
  14. PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xfe
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000001
    
  15. PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0xff
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    
  16. PUSH 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
    PUSH 0x0100
    SAR
    ---
    0x0000000000000000000000000000000000000000000000000000000000000000
    

实现

客户端支持:

  • cpp-ethereum: https://github.com/ethereum/cpp-ethereum/pull/4054

编译器支持:

  • Solidity/LLL: https://github.com/ethereum/solidity/pull/2541

测试

来源:

  • https://github.com/ethereum/tests/tree/develop/src/GeneralStateTestsFiller/stShift

已填充的测试:

  • https://github.com/ethereum/tests/tree/develop/GeneralStateTests/stShift
  • https://github.com/ethereum/tests/tree/develop/BlockchainTests/GeneralStateTests/stShift

版权

CC0 下放弃版权及相关权利。

Citation

Please cite this document as:

Alex Beregszaszi (@axic), Paweł Bylica (@chfast), "EIP-145: EVM 中的按位移位指令," Ethereum Improvement Proposals, no. 145, February 2017. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-145.