常见的基于堆栈的 VM 字节码优化?

Dr.*_*eon 3 c programming-languages bytecode bytecode-manipulation compiler-optimization

好吧,我发布这个是担心它可能会在任何人阅读它之前被关闭 - 我已经习惯了 - 但我会尝试一下......甚至指出我正确的方向或一些现有的答案确实包含一个特定的答案肯定会...


所以,在这个简短的介绍之后......我目前正在为我设计的编程语言编写一个字节码解释器,在C 中,(基于堆栈的 VM)。

如果您想查看支持的操作码,请随时在此处查看:https : //github.com/arturo-lang/arturo/blob/master/src/vm/opcodes.h

堆栈机并没有什么特别之处。值被压入和弹出,操作符和函数处理它们,将计算结果推回堆栈。到现在为止还挺好。

现在,我正处于所有核心功能所在的地步,我正在尝试通过进一步优化来进一步提升它。

这是一个例子(希望是一个相当说明性的例子)。

输入:

fibo: $(x){
    if x<2 {
        return 1
    } {
        return [fibo x-1] + [fibo x-2]
    }
}

i: 0
loop i<34 {
    print "fibo(" + i + ") = " + [fibo i]
    i: i+1
}
Run Code Online (Sandbox Code Playgroud)

产生的字节码

|== Data Segment /======================>
0   : [Func  ]= function <5,1>
1   : [Int   ]= 34
2   : [String]= fibo(
3   : [String]= ) = 
==/ Data Segment =======================|

|== Bytecode Listing /======================>
0   :0       JUMP [Dword] 31
1   :5       LLOAD0
2   :6       IPUSH2
3   :7       CMPLT
4   :8       JMPIFNOT [Dword] 20
5   :13      IPUSH1
6   :14      RET
7   :15      JUMP [Dword] 30
8   :20      LLOAD0
9   :21      IPUSH1
10  :22      SUB
11  :23      GCALL0
12  :24      LLOAD0
13  :25      IPUSH2
14  :26      SUB
15  :27      GCALL0
16  :28      ADD
17  :29      RET
18  :30      RET
19  :31      CPUSH0
20  :32      GSTORE0
21  :33      IPUSH0
22  :34      GSTORE1
23  :35      GLOAD1
24  :36      CPUSH1
25  :37      CMPLT
26  :38      JMPIFNOT [Dword] 61
27  :43      CPUSH2
28  :44      GLOAD1
29  :45      ADD
30  :46      CPUSH3
31  :47      ADD
32  :48      GLOAD1
33  :49      GCALL0
34  :50      ADD
35  :51      DO_PRINT
36  :52      GLOAD1
37  :53      IPUSH1
38  :54      ADD
39  :55      GSTORE1
40  :56      JUMP [Dword] 35
41  :61      END

==/ Bytecode Listing =======================|
Run Code Online (Sandbox Code Playgroud)

对于任何使用过编译器、字节码解释器甚至 JVM 的人来说,上面的代码应该很熟悉。


我想要的是?

想法 - 一般或特定的 - 关于如何进一步优化我的字节码。

例如,每个*2(即:IPUSH2后跟一条MUL指令)被转换为:IPUSH1, SHL因为它是一个更快的操作。

你还有什么建议?是否有任何要优化的此类内容的列表?你能提出一些具体的建议吗?

提前致谢!:)

Ctx*_*Ctx 7

您给出的示例并不是特别好,因为如果进行移位而不是乘法,解释器的性能增益非常低。执行单字节代码指令的开销在几个数量级上超过了这种特定优化的收益。

解释器的最高性能增益是最小化需要执行的指令数量。例如,在可能的情况下,将同一寄存器上的两个连续加法或减法累加到单个操作中。

为了能够进行这种优化,您应该尝试识别所谓的基本块(这些块中要么执行所有指令要么不执行指令,即没有发生跳入或跳出块)并优化指令数量在这些块中,通过将多条指令替换为一条指令,同时保持相同的代码语义。

如果你真的是这个意思,你也可以尝试为你的语言编写一个 gcc 后端,将它编译成字节码;通过这种方式,您可以从 gcc 对中间代码表示 (RTL) 的复杂优化方法中受益。