Golang编译优化——复制传播

本文介绍了复制传播在编程语言优化中的应用,特别是在Golang中的体现,如消除Copy指令以提高效率。通过实例展示了优化过程,包括查找Copy指令的最终引用和处理可能的循环引用,以及具体实现函数如copySource、copyelimValue和copyelim的运作方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、复制传播

复制传播(copy propagation)是一种转换,对于给定的关于变量 x x x y y y的赋值 x ← y x\leftarrow y xy,这种转换用 y y y来代替后面出现的 x x x的引用,只要在这期间没有指令改变 x x x y y y的值。

如下图a的CFG所示,基本块B1中的指令 b ← a b\leftarrow a ba是一个复制赋值。在这条指令, a a a b b b都没有再被赋值,因此 b b b的所有使用都可以用 a a a来代替,图b是对图a做复制传播的结果。
在这里插入图片描述
正是因为复制传播的优化操作,使得 b b b成为无用代码(没有在其他的指令操作数中引用),死代码删除时可以将 b ← a b\leftarrow a ba删除。并且当 a a a是整数值时,这个替换还使得用移位而不是加法来计算赋给 e e e的值成为可能( e ← a + a e\leftarrow a + a ea+a 变成 e ← a < < 1 e\leftarrow a << 1 ea<<1)。

二、Golang 中的应用

Golang中复制传播优化其实就是消除Copy指令,以下是Go编译器对某个代码段编译生成的SSA IR摘选:

b1:-
	...
Plain → b2 (5)

b2: ← b1 b4-
	v9 (5) = Phi <int> v8 v16 (i[int])
	v22 (8) = Phi <int> v7 v14 (r[int])
	v10 (5) = Copy <int> v6 (n[int])
	v11 (+5) = Leq64 <bool> v9 v10
If v11 → b3 b5 (likely) (5)

b3: ← b2-
	v12 (6) = Copy <int> v22 (r[int])
	v13 (6) = Copy <int> v9 (i[int])
	v14 (+6) = Add64 <int> v12 v13 (r[int])
Plain → b4 (6)

编译器在中间代码生成和优化阶段,不可避免的会生成一些复制的指令,如上面b3块中的copy v22 to v12copy v9 to v13。消除Copy指令的操作会遍历所有IR,迭代找到Copy指令的最终引用,将其替换到合适的位置。

下列v14 = Add64 v12 v13,引用参数v12v13会分别替换为其指令的参数v22v9。而v12v13这两条指令如果在其他地方都没有引用,它将变成死代码,会在后续的死代码删除优化(以后会写文章来讲解)中将其消除。

v12 (6) = Copy <int> v22 (r[int])
v13 (6) = Copy <int> v9 (i[int])
v14 (+6) = Add64 <int> v12 v13 (r[int])

Copy 指令消除后 ==>

v12 (6) = Copy <int> v22 (r[int])
v13 (6) = Copy <int> v9 (i[int])
v14 (+6) = Add64 <int> v22 v9 (r[int])

三、具体实现

消除Copy指令的实现逻辑在src/cmd/compile/internal/ssa copyelim.go中,由三个函数来完成。

copySource(v *Value)函数,从Copy指令的参数迭代查找,直至找到第一个非Copy的操作,并将其返回。形如:

for w.Op == OpCopy {
   w = w.Args[0]
}

对于下列代码块,copySource(v2)返回v0

v0 = Def...
v1 = Copy v0
v2 = Copy v1
v3 = Add64 v2 v0

Copy的引用链路可能会形成一个环,比如在一些特殊的情况下,会出现以下情况。这在迭代时就要考虑如何处理这种情况的发生,copySource函数采用了快慢指针来判断是否存在环。如果有环存在,说明这一系列操作是存在歧义的,copySource会将快慢指针的交汇点修改成Unknown,其也将会变成死代码。

v0 = Copy v2    // copy v1也是一个环
v1 = Copy v0
v2 = Copy v1
v3 = Add64 v2 v0

迭代一旦完成,copySource(v *Value)的参数v的指令参数将会被设置成Copy链的第一个非Copy指令值。如下列代码v2的引用参数v1变成了v0,剩下的v1如果在其它地方没有引用,将会变成死代码。

v0 = Def...
v1 = Copy v0
v2 = Copy v1	=> v2 = Copy v0
v3 = Add64 v2 v0

copyelimValue(v *Value) 函数,这个函数确保指令v的所有参数都不是Copy指令。它遍历一个指令的所有参数,如果参数a是Copy,则调用copySource(a)找到Copy链第一个非Copy指令,并用其替换参数a

v0 = Def...
v1 = Copy v0
v2 = Copy v1
v3 = Add64 v2 v0

调用copyelimValue(v3)=>

v0 = Def...
v1 = Copy v0
v2 = Copy v0
v3 = Add64 v0 v0

copyelim(f *Func)函数,它遍历函数中的每个基本块,然后遍历每个基本块中的每个值,并调用copyelimValue函数,该函数确保每个值的参数都不是Copy的结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yelvens

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值