在当前的默认设置下,Solidity 编译器不会将代码转换为任何中间表示(IR)来生成 EVM 字节码,而是直接进行。但是,有一个最近开发的 via-IR 编译管道,它使用 Yul 编程语言作为中间表示。
在高级别上,这两个管道的编译步骤如下所示
在这篇博文中,我们将深入了解 Via-IR 的细节,更好地理解上述每个编译步骤,并讨论将 Via-IR 设为默认的计划。
快速回顾
首先让我们分解 Via-IR 代表什么。
Via: 通过或借助 IR: 中间表示(Solidity 中的 Yul 或内联汇编)
它是 Solidity 中的一个新的编译管道,它通过首先将 Solidity 代码转换为中间表示(Yul)而不是直接将 Solidity 代码编译成 EVM 字节码来引入一个中间步骤。然后可以进一步优化此中间代码,然后再将其转换为最终的 EVM 字节码。
引入基于 IR 的管道是为了不仅允许代码生成更透明和可审计,而且能够实现跨越函数和复杂控制流的更强大的优化过程。
动机与特性
在我们深入了解 Via-IR 的工作原理之前,了解开发 Yul 和由此产生的 IR 管道的动机可能会有所帮助。
如上所述,中间表示语言的目标是生成位于源代码和目标机器代码之间的代码。此代码应该更有利于在最终准备好从汇编生成字节码之前进行进一步处理和优化。中间表示的概念在各种语言编译器中相当常见,例如 Java 的 Java 字节码和 Clang(C++ 编译器)的 LLVM IR。
Yul(以前也称为 JULIA 或 IULIA)是一种中间语言,它被开发为各种后端的 IR。编译器在 via-IR 代码生成器中使用 Yul 作为中间语言。
Yul 的设计目标如下
- 为 Solidity 启用更简单、更规范的代码生成。
- 保持 IR 代码生成器生成的代码的可读性。
- 能够更有效地手动检查、形式化验证和优化代码。
- 使 Yul 到 EVM 的转换尽可能简单直接。
- 适合全程序和高级优化。
- 作为各种编译器(例如,以下编译器)的后端Fe.
总之,为 Solidity 编译器设计 IR 的核心动机是生成更优化的字节码并降低 Gas 成本,以及实现更好的安全审计。
让我们看看 Yul 作为 IR 和新的 via-IR 管道的某些重要特性
- Yul 优化器跨越任意控制流运行,并且不仅在基本控制流块(如分支和循环)内执行优化。在某些情况下,Yul 还可以保留有关复杂控制流中内存/存储的知识。
- Yul 提供了更多内联的机会。此方法允许通过用函数本身的机器代码替换函数调用来提高编译语言的运行时性能,从而消除函数调用的开销。
- Yul 是一种针对多个后端的 IR,最初是 EVM 和 EWASM。目前,它还作为传统 EVM 和 以太坊对象格式 (EOF) 升级的 IR。
- 对于各种第 2 层扩展的复杂调整的易用性使 via-IR 代码生成器更具灵活性。
- 最后但并非最不重要的是,如设计动机中所述,Yul 旨在易于人类阅读。未经优化的和优化的 Yul 输出都可以被视为更低级别的目标,以便更好地进行审计和验证。
深入了解 Via-IR
传统编译
了解如何在没有 Via-IR 的情况下进行默认编译很有帮助。
Solidity 源代码的默认编译管道目前包括以下步骤
- 编译器将每个 Solidity 智能合约源代码作为输入并解析源文件。
- 然后编译器分析源代码并使用传统代码生成器直接生成 EVM 汇编。
- 然后它对代码运行优化器,直到代码被认为足够优化。
- 最后,编译器为每个合约生成字节码。
通过 IR 编译
为了启用用于编译的 via-IR 管道,可以使用以下命令行选项开启:--via-ir 或在标准 JSON 中使用选项 {"viaIR": true}。
通过中间表示(Yul)的编译按以下步骤进行
- 编译器解析 Solidity 源文件。
- 新的 IR 代码生成器不会直接将 Solidity 源代码编译为 EVM 汇编,而是首先将 Solidity 代码转换为 Yul 代码。
- Yul 优化器将重复对 Yul 代码执行优化。
- 然后使用 Yul→evmasm 代码转换将优化的 Yul 代码转换为 EVM 汇编。
- 此代码非常接近实际字节码,但仍适合由 evmasm 优化器进一步优化。因此,默认优化器会像传统管道步骤中一样运行,直到代码得到充分优化。
- 最后,生成 EVM 字节码,就像在传统管道中一样。
挑战与注意事项
虽然选择 IR 有很多优点,但它也并非没有问题。Via-IR 的主要挑战之一是由于对 Yul 代码运行的额外优化步骤导致编译时间更长。
此外,via-IR 代码生成器会无条件地为每个表达式生成代码,而无需代码生成快捷方式。尽管它被认为比 Solidity 源文件更不容易出错,但这也会使未经优化的 IR 代码更冗长且效率低下。可以使用包含单独的、易于验证的和模块化步骤的 Yul 优化器来弥补这一点。可以使用命令行上的 --via-ir --optimize 和 viaIR: true, optimize: {enabled: true}(使用标准 JSON 接口)启用 Yul 优化器。
除此之外,还有一些重要的语义变化。您可以在以下链接中阅读有关这些更改的更多信息:Solidity 官方文档.
将 Via-IR 设为默认
我们在 Solidity 峰会 2023 上分享了将新的 via-IR 管道设为 Solidity 默认编译管道的计划。将 Via-IR 设为默认将使当前的默认管道成为传统管道。
Via-IR 经过了彻底的测试,在安全性方面被认为与传统编译管道相当。IR 管道擅长运行优化并在大多数情况下消除堆栈过深错误。它生成的 Gas 优化代码也优于默认管道。在稳定性能之后,可以进行进一步的优化。从长远来看,这可以使生成的 EVM 代码更节省 Gas。
为了将 Via-IR 设为默认,我们致力于减少编译时间,同时保持较高的优化质量,即实现较低的 Gas 成本,并通过使从 Yul 到 EVM 代码的转换更有效来消除更多堆栈过深错误的案例。
最初的计划是在 2024 年 6 月将 Via-IR 设为默认管道。但是,我们认为将 Via-IR-默认更新与 EOF 升级关联起来最合理,主要有两个原因
- EOF 中无限的交换和复制使 Yul 到 EVM 的转换变得更加简单和高效。这将帮助我们消除更多堆栈过深错误的案例。
- 由于 Via-IR 和 EOF 升级都涉及细微的语义更改,因此更新需要将其作为重大更改。结合发布这两项功能将有助于我们减少 Solidity 开发人员的工作量和升级负担。
接下来是什么?
Via-IR 管道的长期目标是在 Solidity 的下一次迭代中允许从不太灵活的基础模型迁移到标准库,并使新的编译管道(包括优化器)尽可能地面向未来且高效。这将使编译器能够优雅地包含语言需求和 EVM 架构的快速变化。
请继续关注有关 Via-IR 的更多更新,方法是 关注我们的 Twitter 并参与官方 Solidity 论坛 上的社区讨论。