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.