aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/ssa/copyelim.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/ssa/copyelim.go')
-rw-r--r--src/cmd/compile/internal/ssa/copyelim.go102
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
+}