Alert Source Discuss

EIP-6206: EOF - JUMPF 和非返回函数

引入用于链接函数调用的指令。

Authors Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Matt Garnett (@lightclient)
Created 2022-12-21
Discussion Link https://ethereum-magicians.org/t/eip-4750-eof-functions/8195
Requires EIP-4750, EIP-5450

摘要

本 EIP 通过引入一条新的指令 JUMPF 来允许 EOF 函数 (EIP-4750) 中的尾调用优化, 该指令跳转到一个代码段而无需添加新的返回栈帧。

此外,类型部分的格式被扩展为允许将部分声明为非返回,从而简化了 JUMPF 对此类部分进行的堆栈验证。

动机

函数通常会在例程结束时进行调用,然后才返回。JUMPF 通过更改代码段而无需更新返回栈来优化此行为。

在验证时知道一个函数永远不会返回控制权,允许将 JUMPF 视为类似于终止指令,在这种情况下,额外的项目可能会在执行终止时留在操作数栈上。这为编译器提供了生成更优代码的机会,无论是在代码大小还是在花费的 gas 方面。这对于小型错误处理助手尤其有利,它们以 REVERT 结束执行:它们通常在多个分支中重复使用,并且在无需在 JUMPF 到此类助手之前弹出额外的堆栈项目时,将它们提取到助手函数中是高效的。

规范

类型部分更改

我们将非返回部分定义为无法将控制权返回给其调用方的部分。

当相应的代码段是非返回时,类型部分的 outputs 字段包含一个特殊值 0x80。有关验证详细信息,请参见下面的 非返回状态验证

第一个代码段必须具有 0 个输入并且是非返回的。

执行语义

引入了一个新的指令 JUMPF (0xe5)

  1. JUMPF 有一个立即参数 target_section_index,编码为 16 位的无符号大端值。
  2. 如果操作数栈大小超过 1024 - type[target_section_index].max_stack_increase (即,如果被调用的函数可能超过全局堆栈高度限制),则执行将异常停止。这保证了目标函数不会超过全局堆栈高度限制。
  3. JUMPFcurrent_section_index 设置为 target_section_index,并将 PC 设置为 0,但不会更改返回栈。执行在目标部分继续。
  4. JUMPF 的 gas 消耗为 5。
  5. JUMPF 不会弹出或推送任何内容到操作数栈。

如果代码是传统的 bytecode,则 JUMPF 指令会导致异常停止。(注意:这意味着行为没有变化。

代码验证

type[i] 的定义继承自 EIP-4750,并将 stack_height_minstack_height_max 定义为指令流遍历期间某个指令处的堆栈高度边界。

  • JUMPF 的立即参数必须小于代码段的总数。
  • 对于每个 JUMPF 指令:
    • type[current_section_index].outputs 必须大于或等于 type[target_section_index].outputs
    • 或者 type[target_section_index].outputs 必须为 0x80
  • JUMPF 处的堆栈高度验证取决于目标段是否为非返回段:
    • JUMPF 进入返回节(ty​​pe[target_section_index].outputs 不等于 0x80):stack_height_minstack_height_max 必须等于 type[current_section_index].outputs - type[target_section_index].outputs + type[target_section_index].inputs。这意味着,如果当前代码段在堆栈上留下增量 type[current_section_index].outputs - type[target_section_index].outputs 元素,则目标段可以输出比返回堆栈顶部元素调用的原始代码段更少的堆栈元素。
    • JUMPF 进入非返回段(type[target_section_index].outputs 等于 0x80):stack_height_min 必须大于或等于 type[target_section_index].inputs
  • JUMPF 处的堆栈溢出检查:stack_height_max 必须小于或等于 1024 - types[target_section_index].max_stack_increase
  • JUMPF 被认为是终止指令,即在代码验证中没有后继指令,并且可能是该段中的最终指令。
  • 如果任何 RJUMP* 偏移指向紧随 JUMPF 指令之后的两个字节之一,则 EIP-4200 中定义的代码验证也会失败。

CALLF 指令验证已扩展为包括以下规则:

  • 如果任何 CALLF 的立即参数 target_section_index 指向非返回段,即 type[target_section_index].outputs 等于 0x80,则代码段无效。

非返回状态验证

当且仅当该段不包含 RETF 指令且没有以返回段为目标的 JUMPF 指令时,该段类型必须为非返回(通过其在 type 段中的输出值检查目标段的状态)。 注意:这意味着仅包含进入非返回段的 JUMPF 的段本身是非返回段。

理由

允许 JUMPF 跳转到输出较少的部分

JUMPF 堆栈验证的替代规则可能要求目标部分的输出与当前部分的输出完全相同。 在此规则下,特定的目标部分(一段共享的“辅助”代码)将仅“匹配”具有相同数量输出的部分(要求在返回之前执行一些共享的“辅助”代码)。

相反,我们允许从具有更多输出的部分调用给定的 JUMPF 目标部分,只要这些部分自己提供这些额外的堆栈元素(“增量”)。 这将减少重复的代码,因为它将允许编译器在代码生成过程中具有更大的灵活性,以便某些助手可以被函数通用地使用,而不管其输出值如何。

向后兼容性

此更改是向后兼容的,因为 EOF 不允许使用或部署未定义的指令,这意味着不会影响任何合约。

安全考虑

在实施 EOF 容器验证算法期间,需要仔细考虑此新指令。

版权

CC0 下放弃版权及相关权利。

Citation

Please cite this document as:

Andrei Maiboroda (@gumb0), Alex Beregszaszi (@axic), Paweł Bylica (@chfast), Matt Garnett (@lightclient), "EIP-6206: EOF - JUMPF 和非返回函数," Ethereum Improvement Proposals, no. 6206, December 2022. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6206.