package main
func f(p *int, x int, b bool) {
if p == nil {
return
}
g()
if b {
*p = x
} else {
*p = x + 1
}
}
func g()
When compiled, the register allocator spills p and x before the call to g. It then restores both p and x in both the then and else branches. It could instead do the restores before the if b check, which would require only one restore of each.
The tricky part is to do the restores before the if only if we're sure that they won't be dropped again before their use in both branches.
This comes up a lot in write barrier code:
package main
func f(p **int, q *int) {
if p == nil {
return
}
g()
*p = q
}
func g()
p and q are restored in both the write-barrier-on and write-barrier-off branches in the generated code. This is particularly ugly in the write barrier code because the write-barrier-off code block goes from having 0 instructions to having >0 instructions, meaning we need an additional jump as well.
When compiled, the register allocator spills
pandxbefore the call tog. It then restores bothpandxin both thethenandelsebranches. It could instead do the restores before theif bcheck, which would require only one restore of each.The tricky part is to do the restores before the
ifonly if we're sure that they won't be dropped again before their use in both branches.This comes up a lot in write barrier code:
pandqare restored in both the write-barrier-on and write-barrier-off branches in the generated code. This is particularly ugly in the write barrier code because the write-barrier-off code block goes from having 0 instructions to having >0 instructions, meaning we need an additional jump as well.