mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
Make sure we do any just-before-return cleanup on all paths out of a function, including when recovering. Each exit path should include deferreturn (if there are any defers) and then the exit code (e.g. copying heap-escaping return values back to the stack). Introduce a Defer SSA block type which has two outgoing edges - one the fallthrough edge (the defer was queued successfully) and one which immediately returns (the defer had a successful recover() call and normal execution should resume at the return point). Fixes #14725 Change-Id: Iad035c9fd25ef8b7a74dafbd7461cf04833d981f Reviewed-on: https://go-review.googlesource.com/20486 Reviewed-by: David Chase <drchase@google.com>
86 lines
1.8 KiB
Go
86 lines
1.8 KiB
Go
package ssa
|
|
|
|
// phiopt eliminates boolean Phis based on the previous if.
|
|
//
|
|
// Main use case is to transform:
|
|
// x := false
|
|
// if b {
|
|
// x = true
|
|
// }
|
|
// into x = b.
|
|
//
|
|
// In SSA code this appears as
|
|
//
|
|
// b0
|
|
// If b -> b1 b2
|
|
// b1
|
|
// Plain -> b2
|
|
// b2
|
|
// x = (OpPhi (ConstBool [true]) (ConstBool [false]))
|
|
//
|
|
// In this case we can replace x with a copy of b.
|
|
func phiopt(f *Func) {
|
|
for _, b := range f.Blocks {
|
|
if len(b.Preds) != 2 || len(b.Values) == 0 {
|
|
continue
|
|
}
|
|
|
|
pb0, b0 := b, b.Preds[0]
|
|
for len(b0.Succs) == 1 && len(b0.Preds) == 1 {
|
|
pb0, b0 = b0, b0.Preds[0]
|
|
}
|
|
if b0.Kind != BlockIf {
|
|
continue
|
|
}
|
|
pb1, b1 := b, b.Preds[1]
|
|
for len(b1.Succs) == 1 && len(b1.Preds) == 1 {
|
|
pb1, b1 = b1, b1.Preds[0]
|
|
}
|
|
if b1 != b0 {
|
|
continue
|
|
}
|
|
// b0 is the if block giving the boolean value.
|
|
|
|
var reverse bool
|
|
if b0.Succs[0] == pb0 && b0.Succs[1] == pb1 {
|
|
reverse = false
|
|
} else if b0.Succs[0] == pb1 && b0.Succs[1] == pb0 {
|
|
reverse = true
|
|
} else {
|
|
b.Fatalf("invalid predecessors\n")
|
|
}
|
|
|
|
for _, v := range b.Values {
|
|
if v.Op != OpPhi || !v.Type.IsBoolean() || v.Args[0].Op != OpConstBool || v.Args[1].Op != OpConstBool {
|
|
continue
|
|
}
|
|
|
|
ok, isCopy := false, false
|
|
if v.Args[0].AuxInt == 1 && v.Args[1].AuxInt == 0 {
|
|
ok, isCopy = true, !reverse
|
|
} else if v.Args[0].AuxInt == 0 && v.Args[1].AuxInt == 1 {
|
|
ok, isCopy = true, reverse
|
|
}
|
|
|
|
// (Phi (ConstBool [x]) (ConstBool [x])) is already handled by opt / phielim.
|
|
|
|
if ok && isCopy {
|
|
if f.pass.debug > 0 {
|
|
f.Config.Warnl(int(b.Line), "converted OpPhi to OpCopy")
|
|
}
|
|
v.reset(OpCopy)
|
|
v.AddArg(b0.Control)
|
|
continue
|
|
}
|
|
if ok && !isCopy {
|
|
if f.pass.debug > 0 {
|
|
f.Config.Warnl(int(b.Line), "converted OpPhi to OpNot")
|
|
}
|
|
v.reset(OpNot)
|
|
v.AddArg(b0.Control)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|