为 EVM 引入静态相对跳转与调用:EIP-8013(RJUMP / RJUMPI / RJUMPV / RJUMPSUB / RJUMPSUBV)完整解析
为 EVM 引入静态相对跳转与调用EIP-8013RJUMP / RJUMPI / RJUMPV / RJUMPSUB / RJUMPSUBV完整解析【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPsEIP-8013 为以太坊虚拟机EVM新增五条以有符号立即数编码跳转目标地址的指令——RJUMP、RJUMPI、RJUMPV、RJUMPSUB、RJUMPSUBV让编译器能够在绝大多数JUMP/JUMPI场景下使用静态跳转替代动态跳转从而在部署与执行阶段降低 gas 消耗、提升执行性能并大幅改善静态分析的可处理性。读完本文你将掌握这五条指令的完整语义、立即数编码规则、与 EIP-7979 子程序机制return stack /ENTERSUB/RETURNSUB的衔接方式、验证算法扩展、gas 成本建议以及配套的验证与执行测试用例并能在此基础上评估该提案对 EVM 控制流演进的影响。背景与动机为什么 EVM 需要静态跳转EVM 目前只提供动态跳转机制JUMP与JUMPI的跳转目标从数据栈data stack取值指令本身不携带任何目标信息。这种设计只用两条指令就支撑了极其灵活的控制流但代价是代码分析变得困难甚至不可解也直接导致了JUMPDEST标记的诞生。在绝大多数实际场景中控制流本质上是静态的——跳转目标在字节码编译期就已确定根本不需要动态行为。EIP-8013 的提案者总结了几类可以减少动态跳转需求的手段对函数 / 子程序的原生支持一种返回到调用者的指令——即子程序返回subroutine return支持动态索引的switch-case跳转表。本提案引入一个最小特性集恰好涵盖以上三点使编译器可以只使用RJUMP/RJUMPI/RJUMPSUB就完成全部内部控制流的表达。同时该功能不排斥 EVM 未来引入其他形式的控制流也不排斥存量legacy代码继续运行RJUMP/RJUMPI可以与更高级的函数声明方式高效共存用于函数内部的控制流。新指令带来的核心收益有三点部署与执行阶段的 gas 成本更低、执行性能更好、静态分析性质更优。关于静态控制流更完整的历史脉络、技术基础及其对以太坊扩容路线图ZK-Rollup、Optimistic Rollup、RISC-V 迁移的影响可参阅同仓库的姊妹文档 EIPS/eip-8173.mdStatic Control Flow for the EVMInformational 类型。该文档还附带了一个可复现的实验 assets/eip-8173/walk-count/README.md对同一程序分别用传统jump约定与调用/返回指令编写统计分析器必须执行的路径遍历次数——jump版本遍历次数按1 K K² … K^K增长20 次共享调用约为 10²⁶ 次遍历而调用/返回版本恒为 1 次。规范五条新指令提案引入五条新指令每条指令要么携带一个两字节相对偏移要么携带一个偏移表作为立即数immediate指令Opcode立即数栈操作语义RJUMP0xe0relative_offset无将PC设为PC_post_instruction relative_offsetRJUMPI0xe1relative_offset弹出condition将PC设为PC_post_instruction ((condition 0) ? 0 : relative_offset)RJUMPV0xe2max_index relative_offset弹出case将PC设为PC_post_instruction ((case max_index) ? 0 : relative_offset[case])RJUMPSUB0xe3relative_offset无将PC_post_instruction压入return stack并将PC设为PC_post_instruction relative_offset一个ENTERSUB等价于以CALLSUB调用RJUMPSUBV0xe4max_index relative_offset弹出case将PC_post_instruction压入 return stack并将PC设为PC_post_instruction ((case max_index) ? 0 : relative_offset[case])一个ENTERSUB等价于以CALLSUB调用立即数编码规则relative_offset是一个16 位有符号二进制补码大端序值所谓PC_post_instruction指的是整个立即数之后的PC位置即该指令占用完毕后 PC 所指向的下一条指令位置RJUMP的跳转距离为有符号 16 位所能表示的范围最大正向跳转距离为32767。若PC0处的字节码以RJUMP开头则最远可跳到PC32770。RJUMPV与RJUMPSUBV的特殊编码RJUMPV与RJUMPSUBV的立即数编码更为特殊紧跟 opcode 的是一个无符号 8 位max_index决定跳转表的最大索引随后跟有max_index 1个relative_offset值即跳转表最多可容纳 256 个表项RJUMPV的编码必须至少包含一个relative_offset因此其最小长度为4 字节1 字节 opcode 1 字节max_index 至少 2 字节偏移当case max_index索引越界时控制流直接顺序落入下一条指令fall-through。这意味着在大多数使用场景下程序员会把default路径紧跟在RJUMPV指令之后放置一个值得注意的用法是RJUMPV 0 relative_offset等价于一个取反的RJUMPI可在许多场合替代ISZERO RJUMPI relative_offset的组合。与子程序机制的衔接依赖 EIP-7979RJUMPSUB与RJUMPSUBV的目的地址MUST 是ENTERSUB子程序入口标记并且控制权通过RETURNSUB返回到发起调用的RJUMPSUB/RJUMPSUBV。这两条指令相当于直接访问了 EIP-7979 中CALLSUB指令底层的返回调用者机制。EIP-7979 为 EVM 引入的机器状态包括return stack返回栈仅由CALLSUB压入、仅由RETURNSUB弹出最深 1024 项EVM 代码无法直接访问不能读、改、移动从而消除了代码自身篡改控制流的整类漏洞CALLSUB从数据栈弹出目标地址压入返回栈并跳转目标必须是CALLDESTgas 为mid8CALLDEST子程序入口标记功能类似JUMPDEST的空操作gas 为jumpdest1RETURNSUB从返回栈弹出地址写入PC返回栈为空则异常停机gas 为low5。需要说明的是EIP-8013 规范正文中统一使用ENTERSUB指代子程序入口目标anENTERSUB, as if withCALLSUB其底层即 EIP-7979 所描述的调用目标标记机制。EIP-7979 的参考实现以 EELS Python 执行规范形式给出见 EIPS/eip-7979.md 第 333-401 行展示了Evm状态如何新增return_stack字段、jumpdest 分析如何扩展出valid_call_destinations集合以及三条指令的逐条实现可作为理解本提案底层机制的直接参考。验证算法扩展本提案同时扩展了 EIP-7979 的验证算法要求对每一条RJUMP/RJUMPI/RJUMPV/RJUMPSUB/RJUMPSUBV校验其relative_offset指向一条指令不能指向PUSHn/RJUMP/RJUMPI/RJUMPV的立即数数据不能指向代码边界之外进一步地所有且仅有RJUMPSUB与RJUMPSUBV的relative_offset必须指向ENTERSUB而其余指令RJUMP/RJUMPI/RJUMPV允许指向JUMPDEST但并不要求必须如此。由于跳转目标在 jumpdest 分析阶段即被校验完毕运行时无需再次检查这正是静态跳转相比动态跳转在 gas 和执行开销上的优势来源。Gas 成本由于跳转目标在 jumpdest 分析阶段已一次性校验运行时无需重复检查因此这些指令的成本可以低于其动态对应物。提案建议指令建议 gasRJUMP2RJUMPI/RJUMPV4RJUMPSUB/RJUMPSUBV5作为对比当前动态跳转JUMP的成本为mid8gasJUMPDEST为 1 gas。设计权衡Rationale相对寻址Relative addressing选择相对寻址是为了支持可重定位代码relocatable code这意味着代码片段可以被注入。在本提案之前业界已有通过注入PUSHn PC ADD JUMPI来实现同样目标的技巧。相对寻址没有明显缺点并且它使得PC指令有被废弃的可能。立即数大小Immediate size有符号 16 位立即数意味着最大跳转距离为 32767。结合 EIP-170 的MAX_CODE_SIZE 24576与 EIP-3860 的MAX_INITCODE_SIZE 4915216 位立即数被认为足够使用。若改用 8 位立即数PC 最多只能向后移动 125 字节、向前移动 127 字节——对许多 for 循环足够但不足以支撑跨函数跳转。而且 16 位立即数与动态跳转在此类场景下的占用完全一致JUMP PUSH1 n恰好 3 字节因此指令更少是更优选择。若未来确有其他尺寸8 位、24 位、32 位立即数的需求可以像多条PUSH指令那样引入新的 opcode。PUSHn JUMP序列的处理如果选择绝对寻址RJUMP可被视为PUSHn JUMP序列的等价物RJUMPI类似PUSHn JUMPI。那样的话一种观点认为不必引入新指令只需为这类序列提供 gas 折扣、由 EVM 实现自行优化即可。提案认为这不是好方向并将现有PUSHn JUMP序列的语义定义留给 EIP-7979不在本提案中优化其 gas理由如下会进一步复杂化本就繁琐的 gas 计算规则要么需要在共识层定义 EVM 代码的内部表示要么强迫 EVM 实现各自做优化——两者都有风险EVM 实现应当自由选择应用何种优化优化收益并不必须全部让渡给用户还会要求当前依赖流式逐字节执行、无前瞻的实现做出重大改动。无需JUMPDESTLack ofJUMPDESTJUMPDEST有两个用途高效划分代码块——可用于预先计算某个block即两个JUMPDEST之间的指令的总 gas以及 JIT/AOT 翻译显式标记合法跳转位置否则任何非数据位置都可能是跳转目标。对静态跳转而言这两点都不需要分析器在 jumpdest 分析阶段可以直接从静态立即数识别跳转目标。由此带来两个直接收益每个跳转目标省掉 1 字节的JUMPDEST部署时每个目标节省 200 gas执行时每次跳转额外节省 1 gasJUMPDEST自身消耗 1 gas 且跳转时会执行到它。RJUMPV/RJUMPSUBV的默认分支fallback case当RJUMPV或RJUMPSUBV的case在表中找不到匹配即default情形时执行继续顺序进行、不分支。这种设计允许用0填充跳转表中的空隙并让程序员自由选择实现方式。另一种备选方案是在无匹配时触发异常中止但被本提案放弃。向后兼容本变更对向后兼容性不构成任何风险。新指令是全新引入的 opcode对现有 EVM 代码的语义没有任何改动。测试用例提案给出了完整的验证与执行测试用例清单详见 EIPS/eip-8013.md 的Test Cases一节可作为实现与测试驱动开发TDD的直接输入。验证Validation有效用例Valid casesRJUMP/RJUMPI/RJUMPV以JUMPDEST为目标且relative_offset分别为正、负、0RJUMP/RJUMPI/RJUMPV以非JUMPDEST指令为目标且relative_offset分别为正、负、0RJUMPV/RJUMPSUBV的各种合法表大小1 到 256RJUMP作为代码段中的最后一条指令。无效用例Invalid casesRJUMP/RJUMPI/RJUMPV立即数被截断truncatedRJUMPI/RJUMPV作为代码段中的最后一条指令RJUMPSUB/RJUMPSUBV的目标不是ENTERSUB所有五条指令的目标超出代码段边界所有五条指令的目标指向 push 数据所有五条指令的目标指向另一条RJUMP/RJUMPI/RJUMPV的立即数参数。执行ExecutionRJUMPrelative_offset分别为正、负、0RJUMPIrelative_offset分别为正、负、0且分别覆盖condition 0与condition ! 0RJUMPV 0 relative_offsetcase为0与非0RJUMPV表内含正、负、0偏移case为0、非0、越界case max_index落入 default、case 255RJUMPSUBrelative_offset分别为正、负、0RJUMPSUBV 0 relative_offsetcase为0与非0RJUMPSUBV表内含正、负、0偏移case为0、非0、越界、case 255。安全考虑验证算法是安全核心新增带立即数的指令实现验证算法时必须谨慎。静态相对跳转的执行不需要在运行时检查跳转目标这大幅降低了执行成本也因此允许显著降低新指令的 gasDoS 面可控RJUMPV与RJUMPSUBV的相对偏移表最多包含 256 个表项读取一个偏移量不可能成为潜在的 DoS 攻击面。从更广的视角看静态控制流把跳转目标从运行时数据数据栈搬移到了指令编码本身使代码的调用结构对分析工具、编译器与审计工具显式可见。这一点与 EIP-7979 的 return stack 设计一脉相承返回地址存放在 EVM 代码不可触及的独立返回栈中从机制上杜绝了数据覆盖返回地址这类控制流破坏攻击。在仓库中的延伸阅读提案正文EIPS/eip-8013.md底层调用/返回机制return stack、CALLSUB/CALLDEST/RETURNSUB及 EELS 参考实现EIPS/eip-7979.md控制流基础与扩容路线图Informational 背景文档EIPS/eip-8173.md其 CFG 复杂度图示见 assets/eip-8173/control-flow.svg路径遍历计数实验见 assets/eip-8173/walk-count/README.md可用python3 walker.py复现EIP-7979 配套的 RISC-V 基准测量解释执行、寄存器 IR、AOT 编译三档对比assets/eip-7979/riscv/README.md源码位于 assets/eip-7979/riscv/aot.py、interp.c、ir.py等与 Yul 编译器/验证器 assets/eip-7979/yul-compiler/历史前身EOF 容器中的静态相对跳转提案 EIPS/eip-4200.mdRJUMP/RJUMPI/RJUMPV三指令的最早形态仅限 EOF1 代码EIP-8013 将其推广为不依赖 EOF 的通用指令并新增子程序跳转形态RJUMPSUB/RJUMPSUBV。小结EIP-8013 以最小化的五条指令把 EVM 的控制流从只有动态跳转推进到静态为主、动态为辅的形态RJUMP/RJUMPI覆盖绝大部分条件与无条件分支RJUMPV以跳转表实现 switch-case 与默认分支RJUMPSUB/RJUMPSUBV借助 EIP-7979 的 return stack 机制实现可重入的子程序调用与返回。所有跳转目标在验证阶段一次性确认运行时零检查换来部署与执行阶段的双重 gas 节省、更优的静态分析性质以及向 JIT/AOT 编译、RISC-V 迁移与 ZK 证明效率提升等方向展开的演进空间。对实现者而言EIPS/eip-8013.md 中详尽的验证/执行测试用例与 EIPS/eip-7979.md 的 Python 参考实现构成了从语义到落地的完整闭环。【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →