
EIP-7979 的 Yul 子集编译器以代码回答编译器会用 CALLSUB/CALLDEST/RETURNSUB 吗【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPsEIP-7979Call and Return Opcodes for the EVM为 EVM 引入CALLSUB、CALLDEST、RETURNSUB三条控制流指令。本仓库中的 yul-compiler 是这一提案的代码级回答——will compilers use it?——一个把 Yul 子集编译为上述三条指令的编译器并配有 legacy 后端做同口径对比。读完本文你将掌握该编译器的子集语法、双后端编译原理、验证/执行/对比的测试闭环以及实测的字节与 gas 节省数据。一句话定位把函数翻译成三条指令Yul 语言本身就有函数概念但今天的 EVM 没有调用/返回指令编译器只能把函数调用翻译成压返回地址 动态 JUMP把函数返回翻译成经数据栈跳回。这正是 EIP-7979 想解决的问题——动态跳转让控制流对人和工具都不可见。这个仓库的编译器证明了替换方案可行函数定义近一对一地映射为CALLDEST … RETURNSUB函数调用映射为PUSH CALLSUB并且编译产物能通过配套验证 EIPvalidator.py的参考验证器。同时它保留了一个 legacy 后端用今天编译器必须用的方式动态跳转合成调用与返回生成同一批程序用于同口径对比代码大小和 gas。支持的 Yul 子集刻意不聪明compile.py 的模块文档明确划定了子集边界支持零或一个返回值的函数定义、let、赋值、if、带break/continue的for、leave、函数调用以及日常内建函数add/mul/sub/div/sdiv/mod/smod/exp/lt/gt/slt/sgt/eq/iszero/and/or/xor/not/byte/shl/shr/sar/calldataload/calldatasize/pop/mload/mstore/mstore8/stop/return/revert不支持switch、多返回值、优化器约束局部变量必须在DUP16可达深度内即栈上槽位深度超过 15 时compile.py 的dup_of会直接报outside the subset。设计意图在 README 里说得很直白The point is not coverage; it is that nothing about the translation is clever.——重点不是覆盖面而是证明翻译过程毫无取巧。代码生成器是同一套朴素的栈调度器两个后端共享唯一区别只在于调用与返回的发射方式而 7979 后端反而是更简单的那一半一次调用只有两条指令替代四条指令加一个标签一次返回完全不需要地址传递。调用约定的本质差异两个后端的核心分歧在 compile.py 的user_call与 epilogue7979 后端调用方从左到右压参数然后执行CALLSUB被调方的RETURNSUB把返回值留在参数原来所在的位置。legacy 后端调用方先把返回标签压到参数之下再跳转被调方通过这个地址跳回。返回地址和数据混在同一根数据栈上返回必须SWAPJUMP。对照 EIP-7979 主文档 eip-7979.md 中为什么允许 JUMP 落在 CALLDEST一节把调用换成跳转可以消除尾调用g的RETURNSUB直接返回给f的调用者少一条指令、少一个返回地址递归还能在恒定返回栈深度下运行——这正是编译器依赖的尾调用、互递归、状态机与共享尾声shared epilogues变换。这个编译器用实测数据印证了该设计。双端对比的真实数据约 13% 字节、5% gasREADME 的 Measured 一节给出 8 个程序的双端对比表我在当前仓库实际运行python3 test_translator.py得到的输出与此完全一致详见下节测试闭环programbytes savedgas savedsquare18%14%sum of squares24%16%abs12%10%fib (loop-heavy)7%1%factorial (recursive)17%11%sum words8%4%find (break)6%4%guard (leave)13%11%total13%5%节省量与调用密度强相关符合预期以循环为主的fib几乎无收益调用密集的sum of squares收益最大。README 特别强调这是下限数字floor numbers——如果后端做尾调用消除和共享尾声优化收益还会更高。这也与主文档中 square 例子 手算的 29% 更少字节、32% 更少 gas 相互印证程序越大、优化越充分相对差距越小但使用CALLSUB的代码在字节与 gas 上始终不劣于等价旧代码。测试闭环编译→验证→执行→对比README 的 The loop 一节给出入口命令python3 test_translator.pytest_translator.py 对每个示例程序square、sum of squares、abs、fib、factorial (recursive)、sum words、find (break)、guard (leave)同时编译两种后端并断言四件事7979 输出必须通过验证器validator.validate(new)为真legacy 输出必须通不过验证器——因为它的动态返回跳转无法通过静态验证这正是 EIP 要解决的痛点test_translator.py两套字节码在每个测试输入上执行结果完全一致用run7979.py这个内置三条指令的小型 EVM 执行逐一比对(status, returndata)逐行打印字节数与 gas 的并排对比表。每个程序的 calldata 用例也很讲究例如abs同时测正数与负数2**256 - 5的补码表示、factorial覆盖1/5/12、find (break)同时验证找到即 break与找不到返回0xff…ff。我实际运行该脚本8 个程序全部PASS汇总行显示总字节 359 vs 413-13%、总 gas 5507 vs 5847-5%与 README 表完全吻合。解释器带 EIP-7979 运行时语义的最小 EVMrun7979.py 是一个足够执行编译器产物的 EVM 解释器其语义严格对齐 EIP 规范CALLSUB弹出目的地址目的必须是CALLDESTcode[dest] ! CALLDEST即返回CALLSUB to non-CALLDEST停机返回栈已达 1024 则return stack overflow否则压入pc 1并跳转run7979.pyRETURNSUB返回栈为空则return stack underflow否则弹栈设 PCrun7979.pygas 采用 Yellow Paper 分层very low 3、low 5、mid 8、high 10、jumpdest 1加 EIP 提案价CALLSUB8、CALLDEST1、RETURNSUB5见 GAS 表内存扩展不收费——这是刻意的对比发生在同一程序的两次编译之间两者内存使用完全相同去掉该收费才能让差距只反映调用/返回机制的差异run7979.py。它还保留了对 legacy 代码的支持jumpdest_analysis像今天的客户端一样做 JUMPDEST 分析标记动态跳转的合法目的地——The runtime scan validated code no longer needs验证过的代码不再需要的运行时扫描。验证器与操作码表独立成文、逐字节一致validator.py 是配套验证 EIP 的参考验证器。它验证五条约束合法操作码、目的地可证明、返回被正确封装framed returns、无栈下溢、每个指令只到达一个静态栈偏移。其关键设计是遍历被限制在 JUMPDEST 分析找到的指令集合内——PUSH 立即数无论内容如何都不可执行、不可跳转validator.py数据栈深度以CALLDEST为基准相对测量返回点等待被调方的净栈效果跳入/落入CALLDEST则把两个子程序的净效果链接起来需求demand只增不减且以栈上限封顶最坏情况是O(1024 * n)——对代码线性因为 1024 是协议常量validator.py它不证明溢出递归存在时不可判定运行时边界检查保留——这与 EIP-7979 的运行时停机条件一致。opcodes.py 提供共享的操作码表opcode_info()给出每个操作码的 size/pops/pushes/是否终止基本块push_value()读取 PUSH 立即数。表中明确标注CALLSUB: (1, 0, True), CALLDEST: (0, 0, False), RETURNSUB: (0, 0, True)——即CALLSUB弹 1 压 0 且终止基本块CALLDEST是纯标签RETURNSUB不碰数据栈但终止基本块。一个值得注意的工程决策README 的 Files 一节说明validator.py与opcodes.py是从验证 EIP 资产中逐字节复制的精确副本Exact copies: these assets must stand alone必须与原件保持 byte-identical——这样每个资产目录都能独立工作。与主文档的呼应从规范到实现的完整链条把这条资产链路放回 EIP-7979 的整体叙事中看主文档 eip-7979.md 定义语义CALLSUBmid/8 gas弹目的地、压PC1进返回栈、跳转目的地非CALLDEST或返回栈满 1024 则异常停机CALLDESTjumpdest/1 gas纯标签、同时也是合法JUMP/JUMPI目的地RETURNSUBlow/5 gas返回栈空则停机eip-7979.md。占位操作码CALLSUB0xB0、CALLDEST0xB1、RETURNSUB0xB2在编译器、解释器、验证器三处完全一致。主文档的 EELS 参考实现callsub/calldest/returnsub展示了机器状态新增return_stack字段、RETURN_STACK_LIMIT 1024以及单趟get_valid_destinations扫描——本仓库解释器与验证器用可运行代码复现了同一套语义。主文档的 实时性能与 ZK 部分 论述静态控制流让 AOT/JIT 在线性时间内完成分析并用 RISC-V 内核测量给出 5x–51x 的证明成本下降——编译器产出的验证通过代码正是这种分析的前置条件。一句话总结这个编译器仓库不是 EIP-7979 的附庸而是它的试金石——它用真实可运行的 Yul 程序证明一旦有了三条指令编译器可以近乎一对一地发射它们产物可通过静态验证且在字节与 gas 上稳定优于今天的动态跳转合成方案。复现与深入阅读# 在仓库根目录进入资产目录后运行测试闭环 cd assets/eip-7979/yul-compiler python3 test_translator.py关键文件索引yul-compiler/README.md — 编译器设计文档与测量表compile.py — tokenizer、递归下降解析器、双后端代码生成器test_translator.py — 8 个示例程序的编译/验证/执行/对比闭环run7979.py — 带 EIP-7979 语义与 Yellow Paper gas 的小型 EVMvalidator.py / opcodes.py — 验证 EIP 的参考验证器与其操作码表EIPS/eip-7979.md — EIP-7979 完整规范、测试用例与 EELS 参考实现riscv 资产目录 — RISC-V AOT/JIT 与 ZK 证明成本的测量复现【免费下载链接】EIPsThe Ethereum Improvement Proposal repository项目地址: https://gitcode.com/GitHub_Trending/ei/EIPs创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考