mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
This was a cause of some statements being lost. Change-Id: Ia4805c2dafd7a880d485a678a48427de8930d57e Reviewed-on: https://go-review.googlesource.com/c/go/+/198482 Run-TryBot: David Chase <drchase@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Jeremy Faller <jeremy@golang.org>
172 lines
4.2 KiB
Go
172 lines
4.2 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package ssa
|
|
|
|
import "cmd/internal/src"
|
|
|
|
// trim removes blocks with no code in them.
|
|
// These blocks were inserted to remove critical edges.
|
|
func trim(f *Func) {
|
|
n := 0
|
|
for _, b := range f.Blocks {
|
|
if !trimmableBlock(b) {
|
|
f.Blocks[n] = b
|
|
n++
|
|
continue
|
|
}
|
|
|
|
bPos := b.Pos
|
|
bIsStmt := bPos.IsStmt() == src.PosIsStmt
|
|
|
|
// Splice b out of the graph. NOTE: `mergePhi` depends on the
|
|
// order, in which the predecessors edges are merged here.
|
|
p, i := b.Preds[0].b, b.Preds[0].i
|
|
s, j := b.Succs[0].b, b.Succs[0].i
|
|
ns := len(s.Preds)
|
|
p.Succs[i] = Edge{s, j}
|
|
s.Preds[j] = Edge{p, i}
|
|
|
|
for _, e := range b.Preds[1:] {
|
|
p, i := e.b, e.i
|
|
p.Succs[i] = Edge{s, len(s.Preds)}
|
|
s.Preds = append(s.Preds, Edge{p, i})
|
|
}
|
|
|
|
// Attempt to preserve a statement boundary
|
|
if bIsStmt {
|
|
sawStmt := false
|
|
for _, v := range s.Values {
|
|
if isPoorStatementOp(v.Op) {
|
|
continue
|
|
}
|
|
if v.Pos.SameFileAndLine(bPos) {
|
|
v.Pos = v.Pos.WithIsStmt()
|
|
}
|
|
sawStmt = true
|
|
break
|
|
}
|
|
if !sawStmt && s.Pos.SameFileAndLine(bPos) {
|
|
s.Pos = s.Pos.WithIsStmt()
|
|
}
|
|
}
|
|
// If `s` had more than one predecessor, update its phi-ops to
|
|
// account for the merge.
|
|
if ns > 1 {
|
|
for _, v := range s.Values {
|
|
if v.Op == OpPhi {
|
|
mergePhi(v, j, b)
|
|
}
|
|
|
|
}
|
|
// Remove the phi-ops from `b` if they were merged into the
|
|
// phi-ops of `s`.
|
|
k := 0
|
|
for _, v := range b.Values {
|
|
if v.Op == OpPhi {
|
|
if v.Uses == 0 {
|
|
v.resetArgs()
|
|
continue
|
|
}
|
|
// Pad the arguments of the remaining phi-ops so
|
|
// they match the new predecessor count of `s`.
|
|
// Since s did not have a Phi op corresponding to
|
|
// the phi op in b, the other edges coming into s
|
|
// must be loopback edges from s, so v is the right
|
|
// argument to v!
|
|
args := make([]*Value, len(v.Args))
|
|
copy(args, v.Args)
|
|
v.resetArgs()
|
|
for x := 0; x < j; x++ {
|
|
v.AddArg(v)
|
|
}
|
|
v.AddArg(args[0])
|
|
for x := j + 1; x < ns; x++ {
|
|
v.AddArg(v)
|
|
}
|
|
for _, a := range args[1:] {
|
|
v.AddArg(a)
|
|
}
|
|
}
|
|
b.Values[k] = v
|
|
k++
|
|
}
|
|
b.Values = b.Values[:k]
|
|
}
|
|
|
|
// Merge the blocks' values.
|
|
for _, v := range b.Values {
|
|
v.Block = s
|
|
}
|
|
k := len(b.Values)
|
|
m := len(s.Values)
|
|
for i := 0; i < k; i++ {
|
|
s.Values = append(s.Values, nil)
|
|
}
|
|
copy(s.Values[k:], s.Values[:m])
|
|
copy(s.Values, b.Values)
|
|
}
|
|
if n < len(f.Blocks) {
|
|
f.invalidateCFG()
|
|
tail := f.Blocks[n:]
|
|
for i := range tail {
|
|
tail[i] = nil
|
|
}
|
|
f.Blocks = f.Blocks[:n]
|
|
}
|
|
}
|
|
|
|
// emptyBlock reports whether the block does not contain actual
|
|
// instructions
|
|
func emptyBlock(b *Block) bool {
|
|
for _, v := range b.Values {
|
|
if v.Op != OpPhi {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// trimmableBlock reports whether the block can be trimmed from the CFG,
|
|
// subject to the following criteria:
|
|
// - it should not be the first block
|
|
// - it should be BlockPlain
|
|
// - it should not loop back to itself
|
|
// - it either is the single predecessor of the successor block or
|
|
// contains no actual instructions
|
|
func trimmableBlock(b *Block) bool {
|
|
if b.Kind != BlockPlain || b == b.Func.Entry {
|
|
return false
|
|
}
|
|
s := b.Succs[0].b
|
|
return s != b && (len(s.Preds) == 1 || emptyBlock(b))
|
|
}
|
|
|
|
// mergePhi adjusts the number of `v`s arguments to account for merge
|
|
// of `b`, which was `i`th predecessor of the `v`s block.
|
|
func mergePhi(v *Value, i int, b *Block) {
|
|
u := v.Args[i]
|
|
if u.Block == b {
|
|
if u.Op != OpPhi {
|
|
b.Func.Fatalf("value %s is not a phi operation", u.LongString())
|
|
}
|
|
// If the original block contained u = φ(u0, u1, ..., un) and
|
|
// the current phi is
|
|
// v = φ(v0, v1, ..., u, ..., vk)
|
|
// then the merged phi is
|
|
// v = φ(v0, v1, ..., u0, ..., vk, u1, ..., un)
|
|
v.SetArg(i, u.Args[0])
|
|
v.AddArgs(u.Args[1:]...)
|
|
} else {
|
|
// If the original block contained u = φ(u0, u1, ..., un) and
|
|
// the current phi is
|
|
// v = φ(v0, v1, ..., vi, ..., vk)
|
|
// i.e. it does not use a value from the predecessor block,
|
|
// then the merged phi is
|
|
// v = φ(v0, v1, ..., vk, vi, vi, ...)
|
|
for j := 1; j < len(b.Preds); j++ {
|
|
v.AddArg(v.Args[i])
|
|
}
|
|
}
|
|
}
|