EIP-3337: 帧指针支持内存加载和存储操作
Authors | Nick Johnson (@arachnid) |
---|---|
Created | 2021-03-06 |
Discussion Link | https://ethereum-magicians.org/t/eips-3336-and-3337-improving-the-evms-memory-model/5482 |
Requires | EIP-3336 |
Table of Contents
简单总结
引入了四个新的操作码,用于从内存加载数据和将数据存储到由帧指针偏移的内存。
摘要
这个 EIP 引入了四个新的操作码,MLOADFP
,MSTOREFP
,GETFP
和 SETFP
,允许更有效地进行内存访问,内存地址由用户控制的量(称为“帧指针”)偏移。 这使得编译器能够更有效地将临时数据(如局部变量)卸载到内存中,而不是 EVM 的计算堆栈中,这有很多好处,包括有效地消除了对函数中局部变量数量的限制。
动机
在大多数常用的 VM 中,临时数据(如局部变量、函数参数和返回地址)存储在称为堆栈的内存区域中。 与 EVM 的计算堆栈相比,这片内存区域是随机可访问的,因此可以存储任意数量的数据,这些数据可以从它们保持在作用域内的任何位置引用。 尽管这种模型在当前的 EVM 设计中是可能的,但由于内存的线性模型(在 EIP-3336 中讨论了)以及由于缺乏在其他架构中常见的相对内存访问操作码,使得它变得困难。 这个 EIP 提出了新的操作码,允许这种形式的内存使用,而不会给 EVM 实现者或运行时效率带来不必要的负担。
在当前的 EVM 模型中,希望使用这种模式的编译器必须将帧指针(指向当前内存堆栈帧的开始或结束)存储在内存中,并在每次要引用它时加载它。 例如,从由帧指针偏移的内存中加载一个值需要以下操作序列:
Opcode | Gas used |
---|---|
PUSHn x |
3 |
PUSH1 0 |
3 |
MLOAD |
3 |
ADD |
3 |
MLOAD |
3 |
这总共消耗 15 个 gas,并且每次被引用时占用至少 7 个字节的字节码。 相比之下,在这个 EIP 之后,等效的操作序列是:
Opcode | Gas used |
---|---|
PUSH1 x |
3 |
MLOADFP |
3 |
这仅消耗 6 个 gas,并且占用至少 3 个字节的字节码。 EVM 实现所需的工作量是等效的,与常规 MLOAD
相比,仅需一个额外的加法运算。 将值存储在堆栈上的替代方法需要 3 个 gas 和 1 个字节的字节码才能执行 DUPn
操作,但现在最多只有两倍的效率,而不是 5 倍的效率,这使得将值存储在内存中成为一种可行的替代方案。
同样,在这个 EIP 之前,一个帧指针相关的存储需要以下操作序列:
| Opcode | Gas used |
|———–|———-|
| PUSHn x
| 3 |
| PUSH1 0
| 3 |
| MLOAD
| 3 |
| ADD
| 3 |
| MSTORE
| 3 |
这消耗 15 个 gas 和至少 7 个字节的字节码。 在这个 EIP 之后,等效的操作序列是:
Opcode | Gas used |
---|---|
PUSHn x |
3 |
MSTOREFP |
3 |
仅消耗 6 个 gas 和至少 3 个字节的字节码,同时再次仅要求 EVM 实现执行一个额外的加法运算。 将值存储在堆栈上的替代方法需要 6 个 gas 和 2 个字节的字节码来执行 SWAPn POP
序列,这使得它不比内存存储更有效。
规范
参数
Constant | Value |
---|---|
FORK_BLOCK |
TBD |
对于 block.number >= FORK_BLOCK
的区块,以下更改适用。
帧指针
引入了一个新的 EVM 内部状态变量,称为“帧指针”。 这是一个有符号整数,从 0 开始。
SETFP
操作码
引入了一个新的操作码 SETFP
,值为 0x5c
。 此操作码的成本为 G_low
(3 gas),并从堆栈中获取一个参数。 该参数作为帧指针的新值存储。
GETFP
操作码
引入了一个新的操作码 GETFP
,值为 0x5d
。 此操作码的成本为 G_low
(3 gas),并且不接受任何参数。 它获取帧指针的当前值并将其推送到堆栈。
MLOADFP
操作码
引入了一个新的操作码 MLOADFP
,值为 0x5e
。 除了在从内存加载数据之前将帧指针的值添加到地址之外,此操作码在所有方面的行为都与 MLOAD
相同。 尝试从负地址加载数据应被视为与无效操作码相同,消耗所有 gas 并恢复当前执行上下文。
MSTOREFP
操作码
引入了一个新的操作码 MSTOREFP
,值为 0x5f
。 除了在将数据存储到内存之前将帧指针的值添加到地址之外,此操作码在所有方面的行为都与 MSTORE
相同。 尝试将数据存储到负地址应被视为与无效操作码相同,消耗所有 gas 并恢复当前执行上下文。
理由
新操作码的成本
新操作码 MLOADFP
和 MSTOREFP
的成本反映了 MLOAD
和 MSTORE
的成本。 它们的成本通常是等效的,但有一个额外的加法运算例外,这产生的成本可以忽略不计。
新操作码 SETFP
和 GETFP
的成本基于其他常见的低成本操作码,例如 PUSH
和 POP
。
缺少 MSTORE8FP
未包含 MSTORE8FP
操作码,因为预计它将很少使用,并且希望最小化指令集的大小并为将来使用保留操作码。
向后兼容性
此 EIP 专门引入了新的操作码,因此不应影响任何现有程序,除非它们假设这些操作码未定义,我们认为情况并非如此。
安全考虑
DoS 风险通过正确地为操作码定价以反映当前的执行成本来缓解。 没有其他安全考虑因素与此 EIP 相关。
版权
版权和相关权利已通过 CC0 放弃。
Citation
Please cite this document as:
Nick Johnson (@arachnid), "EIP-3337: 帧指针支持内存加载和存储操作 [DRAFT]," Ethereum Improvement Proposals, no. 3337, March 2021. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-3337.