EIP-7937: EVM64 - 64 位模式 EVM 操作码
用于 EVM 中 64 位算术、比较、按位和流操作的多字节操作码。
Authors | Wei Tang (@sorpaas) |
---|---|
Created | 2025-04-23 |
Discussion Link | https://ethereum-magicians.org/t/eip-9687-64-bit-mode-evm-operations/23794 |
Table of Contents
摘要
此 EIP 引入了以 C0
为前缀的多字节操作码,用于 64 位算术 (C001
-C00B
)、比较 (C010
-C015
)、按位 (C016
-C019
) 和流 (C056
和 C057
) 操作。
动机
并非 EVM 中的所有计算都可以利用完整的 256 位整数宽度。因此,拥有一个“64 位模式”以避免不必要的周期可能是有益的。此 EIP 使用“前缀”操作码 C0
,本质上是形成多字节操作码,以避免过多地污染 EVM 操作码空间。
规范
本文档中的关键词“必须(MUST)”、“禁止(MUST NOT)”、“需要(REQUIRED)”、“应为(SHALL)”、“不应为(SHALL NOT)”、“应当(SHOULD)”、“不应当(SHOULD NOT)”、“推荐(RECOMMENDED)”、“不推荐(NOT RECOMMENDED)”、“可以(MAY)”和“可选(OPTIONAL)”应按照 RFC 2119 和 RFC 8174 中的描述进行解释。
前缀操作码行为
此 EIP 使用前缀操作码 C0
,它仅占用这单个 EVM 操作码空间。当解释器遇到操作码 C0
时,它必须继续寻找代码中的下一个字节。然后,它根据下面描述的第二个字节,以“64 位模式”执行操作。如果执行成功,则解释器必须将 PC
增加 2(而不是 1)。
如果第二个字节不是有效的 64 位模式操作,则解释器必须 OOG。
一般 64 位模式行为
在 64 位模式下,所有操作仅作用于每个堆栈值的最低有效 64 位。最高有效 192 位将被丢弃。当结果值被推回堆栈时,它必须确保可观察到的效果将看到最高有效 192 位为零。请注意,这里解释器不必每次都将最高有效 192 位重置为零——如果下一个操作码仍然是 64 位模式,那么最高有效 192 位仍然是不可观察的。我们将在“原理”部分讨论完整的细节。解释器只需要在进入非 64 位模式时再现完整的 256 位值。如果完整的计算密集型部分可以用纯 64 位模式编写,那么这可以带来显着的性能提升。
Gas 成本常量
我们定义以下 gas 成本常量:
G_BASE64
: 1G_VERYLOW64
: 2G_LOW64
: 3G_MID64
: 5G_HIGH64
: 7G_EXP64_STATIC
: 5G_EXP64_DYNAMIC
: 25
算术操作码
64 位模式算术操作码的定义与非 64 位模式相同,只是它仅作用于最低有效 64 位。在下面的定义中,a
、b
、N
是 a mod 2^64
、b mod 2^64
和 N mod 2^64
。
- ADD (
C001
) 和 SUB (C003
):a op b mod 2^64
,gas 成本G_VERYLOW64
。 - MUL (
C002
)、DIV (C004
)、SDIV (C005
)、MOD (C006
)、SMOD (C007
)、SIGNEXTEND (C00B
):a op b mod 2^64
,gas 成本G_LOW64
。 - ADDMOD (
C008
)、MULMOD (C009
):a op b % N mod 2^64
,gas 成本G_MID64
。 - EXP (
C00A
):a EXP b mod 2^64
,gas 成本static_gas = G_EXP64_STATIC, dynamic_gas = G_EXP64_DYNAMIC * exponent_byte_size
。
比较和按位操作码
64 位模式比较和按位操作码的定义与非 64 位模式相同,只是它们仅作用于最低有效 64 位。
- LT (
C010
)、GT (C011
)、SLT (C012
)、SGT (C013
)、EQ (C014
)、AND (C016
)、OR (C017
)、XOR (C018
):a op b mod 2^64
,gas 成本G_VERYLOW64
- ISZERO (
C015
)、NOT (C019
):op a mod 2^64
,gas 成本G_VERYLOW64
- SHL (
C01B
)、SHR (C01C
)、SAR (C01D
):a op N mod 2^64
,gas 成本G_VERYLOW64
请注意:
- 64 位 EQ (
C014
) 可能会对两个不同的整数返回 true,因为它只会比较最低有效 64 位。 - 64 位 ISZERO (
C015
) 可能会对非零 256 位整数返回 true,只要最后 64 位为零。 - BYTE (
1A
) 没有 64 位模式,因为它会影响字节序。
JUMP, JUMPI
对于流操作 JUMP 和 JUMPI,行为如下:
JUMP
将仅从堆栈值中读取最后 64 位。其余 192 位将被丢弃而不读取。Gas 成本为G_MID64
。JUMPI
将仅从堆栈中读取最后 64 位作为目标,并且条件检查将仅读取最后 64 位。Gas 成本为G_HIGH64
。- 在
JUMPDEST
验证短语中,C0
被认为是独立的“模式”操作码,如果跟随的下一个字节是JUMPDEST
,它将继续标记为有效的JUMPDEST
目标。请注意,由于没有 64 位JUMPDEST
,因此在执行期间,C0 JUMPDEST
将导致 OOG。
原理
当智能合约使用 64 位模式时,预计一旦进入,它将希望保持在 64 位模式,并且仅在计算密集型函数完成后才退出到非 64 位模式。此 EIP 的设计特别考虑了这一点。
所有 64 位操作码仅作用于 64 位值。它完全丢弃了其余 192 位。解释器只需要确保当它退出到非 64 位模式并且下次读取值结果时,该值的前 192 位重置为零。因此,EVM 解释器可以使用类型化的堆栈进行优化:
type StackItem = Value U256 | Value64 U64
类型化的堆栈也可以实现为用于内存对齐的位图。
对于 64 位操作码的所有输入,它将读取 Value
(此时它只会获取最后 64 位)或 Value64
(这是所需的)。然后它总是输出 Value64
。在退出到非 64 位模式并且读取 Value64
后,解释器然后通过将前 192 位扩展为零将该值转换回 256 位 Value
。
64 位模式不包含任何依赖于值的字节序的操作码,因此 Value64
也可以存储在架构的最佳字节序中。
64 位模式不会节省任何内存使用量。
讨论
前缀(模式)操作码
此 EIP 还建议我们保留 C0
-CF
用于前缀(模式)操作码。例如,可以设想另外一种模式 OVERFLOW,它可以将算术操作码的行为从包装更改为溢出 OOG,这可以帮助减少例如 SafeMath
所需的额外周期。
优化假设
此 EIP 假设大多数 EVM 实现的目标是原生小端 64 位架构(如 x86_64
、arm64
和 riscv64
)。一个 256 位堆栈项在内部有效地存储为 4 个 64 位无符号整数项 [u64; 4]
。就优化而言,此 EIP 对于这些实现效果最佳。这些操作码只是作用于最后一个 u64
,而不是整个 [u64; 4]
。基本上没有其他变化。
字节序
在 64 位模式下,规范的原则是字节序应严格保留为实现细节。不应该有任何依赖于字节序的操作码。这很重要,因为一方面,我们必须为 64 位和非 64 位操作码启用简单快速的互操作。另一方面,解释器应该能够内部进行优化,无论它使用小端还是大端。
由于字节序问题,此 EIP 目前不包含 64 位模式 BYTE64
、内存操作码 MLOAD64
和 MSTORE64
以及堆栈推送操作码 PUSH*64
(PUSH1
到 PUSH8
)。
可以扩展 EVM64,其中“64 位模式”被定义为“小端 64 位模式”,这可以单独讨论和指定:
BYTE64
将是小端,因此不是(x >> (248 - i * 8)) & 0xFF
,而是(x >> i * 8) & 0xFF
。MLOAD64
和MSTORE64
在内存中加载和存储小端 64 位值。PUSH*64
(PUSH1
到PUSH8
) 接受字面小端字节。
在规范中,该设计没有问题,但这可能会给开发人员带来很大的困惑。因此,作者建议谨慎行事。
POP
, DUP*
, SWAP*
堆栈操作没有显式的 64 位模式:
POP
是“自动 64 位”,因为它仅从堆栈中删除值。DUP*
和SWAP*
将按原样复制或交换针对 64 位优化的堆栈项。它们保持优化状态,因此无需为这些操作码单独设置 64 位模式。
缺点
64 位模式的已知缺点和权衡包括:
- 二进制文件大小会变大。这是显而易见的,因为之前的操作码是单字节,但现在是双字节。每个字节都需要额外的 200 gas 才能存入。但是,64 位模式操作码(通常)更便宜,这为开发人员提供了充分的激励来利用它(最多 200 个调用,并且累积的 gas 成本将更便宜)。
向后兼容性
此 EIP 引入了一个新的(前缀)操作码 C0
。 C0
以前是一个无效的操作码,使用率很低,因此向后兼容性问题微乎其微。
测试用例
测试用例组织为 [stack_item_1, stack_item_2] C0 操作码 => [result_stack_item_1, result_stack_item_2]
。
[ff00000000000000 000000000000000 0000000000000ff 000000000000001, ff0000000000000 000000000000000 0000000000000ff f0000000000000f] C0 SHR => [<0> 780000000000007]
参考实现
待添加。
安全考虑事项
待添加。
需要讨论。
版权
在 CC0 下放弃版权及相关权利。
Citation
Please cite this document as:
Wei Tang (@sorpaas), "EIP-7937: EVM64 - 64 位模式 EVM 操作码 [DRAFT]," Ethereum Improvement Proposals, no. 7937, April 2025. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-7937.