diff options
Diffstat (limited to 'src/cmd/compile/internal/ssa/copyelim.go')
-rw-r--r-- | src/cmd/compile/internal/ssa/copyelim.go | 102 |
1 files changed, 84 insertions, 18 deletions
diff --git a/src/cmd/compile/internal/ssa/copyelim.go b/src/cmd/compile/internal/ssa/copyelim.go index 17471e3b5f..ea888f46f9 100644 --- a/src/cmd/compile/internal/ssa/copyelim.go +++ b/src/cmd/compile/internal/ssa/copyelim.go @@ -4,28 +4,13 @@ package ssa +// combine copyelim and phielim into a single pass. // copyelim removes all uses of OpCopy values from f. // A subsequent deadcode pass is needed to actually remove the copies. func copyelim(f *Func) { - // Modify all values so no arg (including args - // of OpCopy) is a copy. - for _, b := range f.Blocks { - for _, v := range b.Values { - - // This is an early place in SSA where all values are examined. - // Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc. - if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 { - if t.IsStruct() { - v.reset(OpStructMake0) - } else { - v.reset(OpArrayMake0) - } - } - - copyelimValue(v) - } - } + phielim(f) + // loop of copyelimValue(v) process has been done in phielim() pass. // Update block control values. for _, b := range f.Blocks { for i, v := range b.ControlValues() { @@ -93,3 +78,84 @@ func copyelimValue(v *Value) { } } } + +// phielim eliminates redundant phi values from f. +// A phi is redundant if its arguments are all equal. For +// purposes of counting, ignore the phi itself. Both of +// these phis are redundant: +// +// v = phi(x,x,x) +// v = phi(x,v,x,v) +// +// We repeat this process to also catch situations like: +// +// v = phi(x, phi(x, x), phi(x, v)) +// +// TODO: Can we also simplify cases like: +// +// v = phi(v, w, x) +// w = phi(v, w, x) +// +// and would that be useful? +func phielim(f *Func) { + for { + change := false + for _, b := range f.Blocks { + for _, v := range b.Values { + // This is an early place in SSA where all values are examined. + // Rewrite all 0-sized Go values to remove accessors, dereferences, loads, etc. + if t := v.Type; (t.IsStruct() || t.IsArray()) && t.Size() == 0 { + if t.IsStruct() { + v.reset(OpStructMake0) + } else { + v.reset(OpArrayMake0) + } + } + // Modify all values so no arg (including args + // of OpCopy) is a copy. + copyelimValue(v) + change = phielimValue(v) || change + } + } + if !change { + break + } + } +} + +// phielimValue tries to convert the phi v to a copy. +func phielimValue(v *Value) bool { + if v.Op != OpPhi { + return false + } + + // If there are two distinct args of v which + // are not v itself, then the phi must remain. + // Otherwise, we can replace it with a copy. + var w *Value + for _, x := range v.Args { + if x == v { + continue + } + if x == w { + continue + } + if w != nil { + return false + } + w = x + } + + if w == nil { + // v references only itself. It must be in + // a dead code loop. Don't bother modifying it. + return false + } + v.Op = OpCopy + v.SetArgs1(w) + f := v.Block.Func + if f.pass.debug > 0 { + f.Warnl(v.Pos, "eliminated phi") + } + return true +} |