首页
/ EIP-5656 MCOPY 指令详解:为 EVM 引入高效的内存复制原语

EIP-5656 MCOPY 指令详解:为 EVM 引入高效的内存复制原语

2026-09-14 23:54:18作者:范靓好Udolf

EIP-5656(Memory copying instruction)为以太坊虚拟机(EVM)新增了 MCOPY 指令(操作码 0x5E),用于在内存区域之间高效复制数据。本文以该 EIP 为主体,结合本仓库中的 Dencun 硬分叉元 EIP(EIP-7569)identity 预编译 EVM 化提案(EIP-7666) 以及 opcode 表源码 等佐证资料,完整讲解其动机、规范、Gas 定价、语义、测试用例与安全考量。读完本文,你将掌握 MCOPY 的栈接口与定价公式、它与 CALLDATACOPY/RETURNDATACOPY 的关系,以及为何它是替代 identity 预编译进行内存复制的更优方案。

概述:一条指令解决内存复制的开销问题

MCOPY 是一条用于复制 EVM 内存区域的指令。在它出现之前,EVM 上实现内存复制只能依赖两种间接手段:调用 identity 预编译(通过 CALL 的输入/输出内存偏移实现复制),或用 MLOAD/MSTORE 逐字搬运。前者伴随高昂的调用开销,后者对非整字复制(partial-word copy)需要加载、掩码、按位或再存储,代价显著。MCOPY 将这些操作封装为单条指令,成本为 Gverylow + Gcopy * ceil(length / 32),一次复制 256 字节仅需 27 gas。

该指令随 Dencun(Cancun)网络升级在主网激活(主网激活时间戳 1710338135),属于执行层(Execution Layer)变更。本仓库中的 EIP-7569 明确将其列为 Dencun 升级包含的 EIP 之一。

背景与动机:EVM 上内存复制的历史困境

identity 预编译方案及其成本变迁

早期开发者通过 identity 预编译实现内存复制:利用 CALL 的输入内存偏移和输出内存偏移完成搬运。其成本为 15 + 3 * (length / 32) gas,外加调用开销。但随着 CALL 的成本上调至 700 gas,identity 预编译方案变得不再经济;后续 EIP-2929CALL 的冷/热账户访问成本调整为 2600/100,使预编译方案略有改善,但仍不足以成为合理选择。

关于 EIP-2929:它在 accessed_addresses 集合中记录已访问账户,首次冷访问收取 COLD_ACCOUNT_ACCESS_COST(2600),同一交易内再次访问只收 WARM_STORAGE_READ_COST(100)。由于预编译地址被预置于访问集合中,identity 预编译的调用成本因此降为 100 + 15 + 3 * (length / 32)。

MLOAD/MSTORE 逐字搬运的局限

复制整字(exact words)可以用 <offset> MLOAD <offset> MSTORE<offset> DUP1 MLOAD DUP2 MSTORE 实现,每个字至少 12 gas。若偏移量在编译期已知且可展开循环(unrolled),这相当高效;但若在运行时以任意起始偏移实现复制,除控制流开销外,还需用 32 ADD 递增偏移,每个字至少再增加 6 gas。

复制非整字(non-exact words)更棘手:最后一个部分字需要同时加载源与目标、做掩码与或运算、再写回。唯一的高效边界情况是最后"部分字"恰好为单字节时,可用 MSTORE8 高效存储。

量化对比:复制 256 字节的 Gas 成本

EIP 给出了复制 256 字节的典型成本对比:

方案 最低 Gas
identity 预编译(EIP-2929 之前) 至少 757
identity 预编译(EIP-2929 之后) 至少 157
展开的 MLOAD/MSTORE 序列 至少 96
本 EIP 的 MCOPY 27

实际收益与生态影响

根据对区块 10537502 至 10538702 的分析,约 10.5% 的内存复制操作本可通过 MCOPY 获得性能提升。内存复制是 Solidity、Vyper 等语言的基础操作,EIP 预期该指令能为构建数据结构(包括切片访问与内存对象复制)提供高效手段,同时为未来 CALL 指令的 Gas 再定价提供前瞻性保护。

此外,专用 MCOPY 指令还降低了静态分析与优化的难度:CALL 的副作用必须被整体围栏(fenced)处理,而 MCOPY 已知只影响内存。即使为预编译添加特例,未来硬分叉仍可能改变 CALL 的语义,使基于 identity 预编译的分析仅在特定区块范围内有效。最后,EIP 明确指出内存复制对 EVM384 这类计算密集型操作至关重要,是其中一项显著开销。

规范:操作码、栈接口与语义

操作码与输入栈

MCOPY 指令的操作码为 0x5E,其输入栈布局如下(栈顶为 top - 0):

栈位置
top - 0 dst
top - 1 src
top - 2 length

这一顺序与其他复制指令(CALLDATACOPYRETURNDATACOPY)保持一致。本仓库 assets/eip-8011/compute_mem_ops.md 中的常量定义也印证了这一点:CALLDATACOPY = 0x37CODECOPY = 0x39RETURNDATACOPY = 0x3EMCOPY = 0x5E,它们同属"计算与内存操作"家族。

assets/eip-8337/opcodes.py 的 EVM 操作码表可以看出,仓库对 MCOPY 的元数据描述为 (3, 0, False),即弹出 3 个栈项(dstsrclength)、压入 0 个栈项、且不终止基本块——与 CALLDATACOPYCODECOPYRETURNDATACOPY 完全一致:

**{op: (3, 0, False) for op in (0x37, 0x39, 0x3E, 0x5E)}, # CALLDATACOPY, CODECOPY, RETURNDATACOPY, MCOPY

Gas 定价

按黄皮书术语,MCOPY 属于 W_copy 操作码组,遵循黄皮书中 W_copy 的 Gas 计算公式。以 EIP 编写时的参考公式为准:

words_copied = (length + 31) // 32
g_verylow    = 3
g_copy       = 3 * words_copied + memory_expansion_cost
gas_cost     = g_verylow + g_copy

Gverylow + Gcopy * ceil(length / 32),其中 Gverylow = 3Gcopy = 3,与黄皮书中 Wcopy 指令族的定价规则一致(黄皮书中的计算为准)。

语义

  • length 字节从 src 指向的偏移复制到 dst 指向的偏移;
  • 复制过程如同经过一个中间缓冲区(intermediate buffer),因此允许源与目标内存区域重叠
  • length > 0src + lengthdst + length 超出当前内存长度,则按相应 Gas 成本扩展内存(memory expansion)。

输出栈

本指令不产生任何栈输出项。

设计理由

EIP 指出,整字与部分字内存复制的生产级实现可参见 Solidity、Vyper 和 Fe 编译器。与 EIP-2929 配合后,虽然 identity 预编译的调用开销从 700 降至 100 gas,但对于将其重新作为合理的复制替代方案而言仍过于昂贵——这正是推动专用指令的根本原因。

向后兼容性

本 EIP 引入了一条此前不存在的新指令。已经部署的合约若使用了该操作码(0x5E),在本 EIP 生效后其行为可能发生变化。由于 0x5E 在 Dencun 之前是无效操作码,普通合约不受影响,只有字节码中恰好包含 0x5E 字节的合约可能面临行为变更(此类合约在 Dencun 前的执行会立即回滚,升级后则正常执行)。

测试用例

EIP 提供了四个基础测试向量,覆盖正常复制、自复制与双向重叠复制场景。所有用例的 gas used 均为 6(3 个 verylow 级操作消耗)。

MCOPY 0 32 32:从偏移 32 复制 32 字节到偏移 0

pre(空格仅为可读性插入):

0000000000000000000000000000000000000000000000000000000000000000 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

post:

000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f

gas used: 6

MCOPY 0 0 32:从偏移 0 复制 32 字节到偏移 0(自复制)

pre:

0101010101010101010101010101010101010101010101010101010101010101

post:

0101010101010101010101010101010101010101010101010101010101010101

gas used: 6

MCOPY 0 1 8:从偏移 1 复制 8 字节到偏移 0(重叠)

pre(空格在字节 8 处):

0001020304050607 080000000000000000000000000000000000000000000000

post:

0102030405060708 080000000000000000000000000000000000000000000000

gas used: 6

MCOPY 1 0 8:从偏移 0 复制 8 字节到偏移 1(反向重叠)

pre(空格在字节 8 处):

0001020304050607 080000000000000000000000000000000000000000000000

post:

0000010203040506 070000000000000000000000000000000000000000000000

gas used: 6

注意上面两个重叠用例验证了"如同使用中间缓冲区"的语义:无论源在前还是目标在前,复制结果都与先完整读出再写入等价,而不是逐字节原地搬运。

EIP 还指出,完整的测试套件可在执行规范测试(execution spec tests)的 Cancun 目录下 eip5656_mcopy 套件中找到(详见 EIP 正文中的说明)。

在 Dencun 升级中的落地

本仓库中的 EIP-7569(Hardfork Meta - Dencun)EIP-5656 列为 Dencun 网络升级包含的执行层 EIP 之一(与 EIP-1153 瞬态存储、EIP-4844 Blob 交易等并列)。其激活时间如下:

网络 激活 Epoch 激活时间戳
Goerli 231680 1705473120
Sepolia 132608 1706655072
Holešky 29696 1707305664
Mainnet 269568 1710338135

后续演进:identity 预编译的 EVM 化

MCOPY 的引入为生态提供了放弃 identity 预编译的契机。本仓库中的 EIP-7666(EVM-ify the identity precompile) 明确指出:identity 预编译最初就是为了弥补"没有直接复制内存的操作码"而引入的;自 MCOPY 出现后,可以移除 0x04 地址处的 identity 预编译,代之以一段极简 EVM 代码 0x365f5f37365ff3,其对应指令序列为:

CALLDATASIZE PUSH0 PUSH0 CALLDATACOPY CALLDATASIZE PUSH0 RETURN

即把 calldata 复制进内存后原样返回,功能与 identity 预编译等价。这体现了"专用指令取代预编译"的演进路径,也为未来淘汰其他低使用率预编译(如部分哈希函数与 MODEXP)铺平了道路。

安全考量

  • 中间缓冲区问题:客户端实现不应使用中间缓冲区来完成复制(正如 C 标准库的 memmove 不使用中间缓冲区),否则可能成为拒绝服务(DoS)向量。大多数语言内置的字节搬移函数(如 memmove)在性能特性上是正确的,可作为实现参考。
  • 内存扩展定价:除上述注意点外,MCOPY 的 DoS 与内存耗尽分析与所有触碰内存的操作码一致,因为其内存扩展遵循相同的定价规则。

总结

MCOPY0x5E)是 Dencun 升级为 EVM 带来的基础内存复制原语:它以 dstsrclength 三个栈参数完成带重叠保护的内存复制,定价为 Gverylow + Gcopy * ceil(length / 32),复制 256 字节仅需 27 gas。相比 identity 预编译(约 157 gas)与展开的 MLOAD/MSTORE(约 96 gas),其效率优势显著,并为 Solidity/Vyper 编译器、静态分析器与 EVM384 等重计算场景提供了坚实支撑。理解 MCOPY 的语义与定价,是分析 Dencun 后合约 Gas 行为、评估预编译演进路线的基础。


本文版权遵循原文档的声明,相关权利依据 LICENSE.md 中的 CC0 条款放弃。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
34
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.21 K
2.81 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
945
1.86 K
docsdocs
暂无描述
Markdown
906
5.84 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
537
607
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
864
1.36 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
4.28 K
1.03 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.39 K
1.48 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
550
401
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.19 K
347