github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/ssa/tighten.go (about) 1 // Copyright 2015 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package ssa 6 7 // tighten moves Values closer to the Blocks in which they are used. 8 // This can reduce the amount of register spilling required, 9 // if it doesn't also create more live values. 10 // For now, it handles only the trivial case in which a 11 // Value with one or fewer args is only used in a single Block, 12 // and not in a phi value. 13 // TODO: Do something smarter. 14 // A Value can be moved to any block that 15 // dominates all blocks in which it is used. 16 // Figure out when that will be an improvement. 17 func tighten(f *Func) { 18 // For each value, the number of blocks in which it is used. 19 uses := make([]int32, f.NumValues()) 20 21 // For each value, whether that value is ever an arg to a phi value. 22 phi := make([]bool, f.NumValues()) 23 24 // For each value, one block in which that value is used. 25 home := make([]*Block, f.NumValues()) 26 27 changed := true 28 for changed { 29 changed = false 30 31 // Reset uses 32 for i := range uses { 33 uses[i] = 0 34 } 35 // No need to reset home; any relevant values will be written anew anyway. 36 // No need to reset phi; once used in a phi, always used in a phi. 37 38 for _, b := range f.Blocks { 39 for _, v := range b.Values { 40 for _, w := range v.Args { 41 if v.Op == OpPhi { 42 phi[w.ID] = true 43 } 44 uses[w.ID]++ 45 home[w.ID] = b 46 } 47 } 48 if b.Control != nil { 49 uses[b.Control.ID]++ 50 home[b.Control.ID] = b 51 } 52 } 53 54 for _, b := range f.Blocks { 55 for i := 0; i < len(b.Values); i++ { 56 v := b.Values[i] 57 switch v.Op { 58 case OpPhi, OpGetClosurePtr, OpConvert, OpArg: 59 // GetClosurePtr & Arg must stay in entry block. 60 // OpConvert must not float over call sites. 61 // TODO do we instead need a dependence edge of some sort for OpConvert? 62 // Would memory do the trick, or do we need something else that relates 63 // to safe point operations? 64 continue 65 default: 66 } 67 if v.Op == OpSelect0 || v.Op == OpSelect1 { 68 // tuple selector must stay with tuple generator 69 continue 70 } 71 if len(v.Args) > 0 && v.Args[len(v.Args)-1].Type.IsMemory() { 72 // We can't move values which have a memory arg - it might 73 // make two memory values live across a block boundary. 74 continue 75 } 76 if uses[v.ID] == 1 && !phi[v.ID] && home[v.ID] != b && len(v.Args) < 2 { 77 // v is used in exactly one block, and it is not b. 78 // Furthermore, it takes at most one input, 79 // so moving it will not increase the 80 // number of live values anywhere. 81 // Move v to that block. 82 c := home[v.ID] 83 c.Values = append(c.Values, v) 84 v.Block = c 85 last := len(b.Values) - 1 86 b.Values[i] = b.Values[last] 87 b.Values[last] = nil 88 b.Values = b.Values[:last] 89 changed = true 90 } 91 } 92 } 93 } 94 } 95 96 // phiTighten moves constants closer to phi users. 97 // This pass avoids having lots of constants live for lots of the program. 98 // See issue 16407. 99 func phiTighten(f *Func) { 100 for _, b := range f.Blocks { 101 for _, v := range b.Values { 102 if v.Op != OpPhi { 103 continue 104 } 105 for i, a := range v.Args { 106 if !a.rematerializeable() { 107 continue // not a constant we can move around 108 } 109 if a.Block == b.Preds[i].b { 110 continue // already in the right place 111 } 112 // Make a copy of a, put in predecessor block. 113 v.SetArg(i, a.copyInto(b.Preds[i].b)) 114 } 115 } 116 } 117 }