为什么这个 Cairo 程序将 2 的幂放入内存中?

Jai*_*ken 9 cairo-lang starknet

我正在尝试解决“开罗如何运作”教程中的这个额外问题。我运行了以下函数,打开 Cairo 跟踪器,发现内存已满,且为 2 的幂。这是为什么呢?

func main():
    [fp + 1] = 2; ap++
    [fp] = 5201798304953761792; ap++
    jmp rel -1
end
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

Dan*_*mon 5

以下是一些可以帮助您找到答案的主要问题。休息后回答问题:

  1. 指令跳转到哪里jmp rel -1
  2. 目标指令的作用是什么?之后会发生什么?
  3. 这条指令是如何进入内存的程序部分的?

  1. jmp rel -1被编码在存储器中的地址5-6处。当它执行时,我们有pc = 5,因此跳转后我们将执行位于 的指令pc = 4,即0x48307fff7fff8000
  2. 该字节码对指令进行编码[ap] = [ap - 1] + [ap - 1]; ap++(要检查,您可以手动解码标志和偏移量[编辑:见下文],或者简单地使用该指令编写一个 cairo 程序并查看它编译成什么)。执行完后,pc就加1,所以我们再次执行jmp rel -1,以此类推,无限循环。应该清楚为什么这会用 2 的幂填充内存(前 2 个位于地址 10 处,由指令写入[fp + 1] = 2; ap++)。
  3. 该指令[fp] = 5201798304953761792; ap++有一个立即参数(右侧,5201798304953761792)。带有立即参数的指令在存储器中被编码为两个字段元素,第一个编码通用指令(例如[fp] = imm; ap++),第二个是立即值本身。因此这个立即数被写入地址4中,实际上5201798304953761792与 相同0x48307fff7fff8000。类似地,2at 地址 2 是指令 的立即数[fp + 1] = 2,而-1at 地址 6 是指令 的立即数jmp rel -1

总而言之,这种奇怪的行为是由于相对跳转移动到立即值的地址并将其解析为独立指令所致。通常不会发生这种情况,因为pc在执行带有立即值的指令后加 2,而在执行没有立即值的指令时加 1,因此它总是继续到下一个编译指令。为了到达这个意外的程序计数器,这里需要无标签的跳转。


如何手动解码 的标志和偏移量0x48307fff7fff8000?查阅开罗白皮书(主要是第 50-59 页),我们看到较低的三个 16 位字编码偏移量 off dst = 0, off op0 = off op1 = -1 (值0x8000, 0x7fff,0x7fff偏移 2 15,或者可以被视为有符号整数,如第 51 页所述)。标志字为0x4830,有 4 个标志设置为 1,其余为 0:设置的标志从最少到最多依次为 f 4、 f 5、 f 11和 f 14,分别对应于标志OP1_APRES_ADDAP_ADD1OPCODE_ASSERT_EQ(根据至第 58 页)。让我们探讨一下这些标志的含义(源自第 58-59 页中列出的约束):

  • OP1_AP标志表示操作数 1 是相对于 获取的ap,偏移量为op1,即op1 = [ap - 1]。默认情况下,操作数 0 和dst也相对于ap(当未设置相关标志时),并且包括上面的偏移量,我们看到op0 = [ap - 1], dst = [ap]
  • 该标志表示和RES_ADD之间的运算是加法,即强制执行约束。op0op1res = [ap - 1] + [ap - 1]
  • OPCODE_ASSERT_EQ标志意味着这是作为相等断言命令,这意味着将通过 enforcingres等于,我们现在看到它相当于。dstdst - res = 0[ap] = [ap - 1] + [ap - 1]
  • 最后,该AP_ADD1标志仅意味着ap前进 1,它对应于ap++命令的部分。

总而言之,我们得到了[ap] = [ap - 1] + [ap - 1]; ap++所声称的命令。