通过L / R操作码实现结构化跳过区域
抽象的
这引入了两个新的 EVM 操作码, L (左分隔符)和R (右分隔符) ,它们定义了字节码中的结构化跳过区域。
执行时, L会导致 EVM 跳过其包含的字节,直接跳到匹配的R这些区域允许合约将不可执行的结构化数据直接嵌入字节码中,同时保持确定性执行。
动机
EVM 字节码目前没有原生方法来区分:
- 可执行指令
- 嵌入式元数据
- 编译表
- 结构常数
- 仅适用于链下工具的替代代码路径
所有字节都被视为潜在可执行指令,跳过代码的唯一方法是通过动态跳转。
这会导致以下问题:
- 编译器无法安全地将结构化数据嵌入字节码中。
- 静态工具必须保守地将所有字节都视为可执行文件。
- 字节码无法承载复杂的内部结构而不冒着意外执行的风险。
- 控制流必须使用非结构化跳转来表达。
我们提出了一种结构化的跳过区域,其行为类似于字节码内部的未执行岛屿。
这些区域允许字节码安全地包含:
- 查找表
- 跳板
- 类型元数据
- 类似ABI的内部描述符
- 编译器提示
- 未来扩展有效载荷
不影响执行。
规格
新操作码
| 操作码 | 姓名 | 堆 | 描述 |
|---|---|---|---|
0x?? | L | — | 开始跳过区域 |
0x?? | R | — | 跳过区域 |
语义学
1. L — 跳到匹配的R
当 EVM 执行操作码L时:
- 它进入跳过模式
- 它向前扫描字节码以查找匹配的
R - 中间的所有字节都会被忽略,永远不会执行。
- 嵌套的
L/R对必须平衡
匹配算法
depth = 1pc = pc + 1while depth > 0:opcode = code[pc]if opcode == L:depth += 1 else if opcode == R:depth -= 1pc += 1 2. R — 结构分隔符
执行从匹配的R之后的第一个操作码处恢复。
如果找不到匹配的R → 异常终止。
结构规则
1. 区域是不可执行的
L … R区域内的字节:
- 绝不能执行
- 被视为不透明有效载荷
- 可能包含任意字节值,包括无效的操作码
2. 跳转到或跳出不同区域并不被禁止
这样做是合理的:
-
JUMP到JUMPI区域内的位置 - 从区域内部跳到区域外部
3. 允许嵌套
跳过区域可以嵌套:
L<data or metadata>L<more data> R R匹配算法确保配对正确。
汽油成本
| 操作码 | 气体 |
|---|---|
L | 3 |
R | 2 |
客户端可以预处理字节码以映射匹配对,从而使L能够在恒定时间内执行。
理由
字节码作为一种结构化容器
该提案允许合约将字节码视为容器格式,而不仅仅是指令。
使用L/R ,字节码可以安全地包含:
| 用例 | 例子 |
|---|---|
| 常量数据块 | 预先计算的表格,大型常数 |
| 跳板 | 密集交换机调度表 |
| 类似ABI的内部布局 | 内部领域特定语言的类型描述符 |
| 调试信息 | 源图,符号名称 |
| 编译器元数据 | 优化提示、布局信息 |
| 版本化扩展 | 向前兼容的有效载荷区域 |
所有这一切都无需冒意外处决的风险。
为什么不直接使用JUMP呢?
使用JUMP跳过数据:
- 需要动态控制流
- 使跳过的字节在语法上可执行。
- 使静态分析变得模糊不清
- 不能安全地包含任意字节模式
L/R则创建显式的非可执行区域,类似于传统二进制文件中的数据段。
为什么不重复使用JUMPDEST ?
JUMPDEST标记有效的跳转目标,但不标记不可执行区域。
我们需要相反的方法:一种声明“这不是代码”的方法。
向后兼容性
完全向下兼容:
- 现有合同不包含
L或R - 旧字节码行为未改变
- 新的语义仅在存在操作码时适用。
示例:嵌入数据表
PUSH1 0 x00SSTOREL 0 x12 0 x34 0 x56 0 x78 0 x9a 0 xbc 0 xde 0 xf0 R PUSH1 0 x01SSTORE L … R内部的字节永远不会被执行,可以通过链下工具读取,也可以通过EXTCODECOPY复制代码来读取。
结论
L和R向 EVM 引入了结构化非可执行区域,使字节码能够作为代码和结构化数据的混合体发挥作用。
这:
- 提高可分析性
- 实现更安全的编译器生成的元数据
- 允许使用密集的字节码内数据结构
- 保持完全向后兼容性
- 执行模型只需做极少改动
只需添加一个小小的操作码,EVM 字节码就能变成一个自描述的、结构化的产物,而不仅仅是一个扁平的指令流。

