乐观者

本节讨论第一次添加到Solidity中的优化程序,它在操作码流上运行。有关新的基于Yul的优化程序的信息,请参阅 readme on github .

坚固性优化程序对装配进行操作。它将指令序列拆分为 JUMPsJUMPDESTs .在这些块中,优化程序分析指令,并将对堆栈、内存或存储的每一次修改记录为一个表达式,该表达式由一条指令和一组参数组成,这些参数是指向其他表达式的指针。乐观者使用一个名为“CommonSubExpressioneLiminator”的组件,在其他任务中,找到始终相等的表达式(在每个输入上),并将它们组合到一个表达式类中。乐观主义者首先尝试在已经知道的表达式列表中找到每个新的表达式。如果这不起作用,它会根据如下规则简化表达式 constant + constant = sum_of_constantsX * 1 = X .因为这是一个递归过程,所以如果第二个因子是一个更复杂的表达式,我们也可以应用后一个规则,因为我们知道它总是计算为一个。对存储器和存储器位置的修改必须删除关于存储器和存储器位置的知识,这些知识是已知的,是不同的。如果我们先写到位置x,然后写到位置y,两者都是输入变量,那么第二个变量可以覆盖第一个变量,所以我们不知道在写到y之后x中存储了什么。如果将表达式x-y简化为一个非零常量,我们知道我们可以保留我们的知识ab。在X处存储的内容。

在这个过程之后,我们知道哪些表达式必须在栈的末尾,并有一个对内存和存储的修改列表。这些信息与基本块一起存储,并用于链接它们。此外,有关堆栈、存储和内存配置的知识将转发到下一个块。如果我们知道所有人的目标 JUMPJUMPI 说明:我们可以建立一个完整的程序控制流程图。如果只有一个我们不知道的目标(原则上这可以发生,跳跃目标可以从输入中计算),我们必须清除有关块输入状态的所有知识,因为它可能是未知的目标 JUMP .如果乐观者发现 JUMPI 其条件计算为常量,它将其转换为无条件跳转。

最后一步是重新生成每个块中的代码。乐观器从块末尾堆栈上的表达式创建依赖关系图,并删除不属于此图的每个操作。它生成的代码按照原始代码的顺序将修改应用于内存和存储(删除发现不需要的修改)。最后,它生成所有需要在堆栈上正确位置的值。

这些步骤应用于每个基本块,如果新生成的代码较小,则将其用作替换代码。如果基本块在 JUMPI 在分析过程中,条件评估为一个常量, JUMPI 根据常量的值替换。因此代码类似

uint x = 7;
data[7] = 9;
if (data[x] != x + 2)
  return 2;
else
  return 1;

仍然可以简化为可以编译的代码,即使这些指令在进程开始时包含一个跳转:

data[7] = 9;
return 1;