github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/pogo/peephole.go (about) 1 // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors 2 // Use of this source code is governed by an MIT-style 3 // license that can be found in the LICENSE file. 4 5 package pogo 6 7 import ( 8 "fmt" 9 "go/token" 10 "go/types" 11 "strings" 12 13 "golang.org/x/tools/go/ssa" 14 ) 15 16 // InlineMap reads the value of a key, tracking if it has been seen 17 func (comp *Compilation) InlineMap(key string) (val string, ok bool) { 18 val, ok = comp.inlineMap[key] 19 count, seen := comp.keysSeen[key] 20 if seen { 21 comp.keysSeen[key] = count + 1 22 } else { 23 comp.keysSeen[key] = 1 24 } 25 return 26 } 27 28 func (comp *Compilation) newInlineMap() { 29 comp.inlineMap = make(map[string]string) 30 comp.keysSeen = make(map[string]int) 31 } 32 33 // peephole optimizes and emits short sequences of instructions that do not contain control flow 34 func (comp *Compilation) peephole(instrs []ssa.Instruction) { 35 comp.newInlineMap() 36 for i := 0; i < len(instrs); i++ { 37 //var v ssa.Value 38 var isV, inline bool 39 if len(instrs[i:]) >= 1 { 40 for j := len(instrs); j > (i /* +1 */); j-- { 41 opt, reg := comp.peepholeFindOpt(instrs[i:j]) 42 if opt != "" { 43 //fmt.Println("DEBUG PEEPHOLE", opt, reg) 44 fmt.Fprintln(&LanguageList[comp.TargetLang].buffer, 45 LanguageList[comp.TargetLang].PeepholeOpt(opt, 46 reg, instrs[i:j], "[ PEEPHOLE ]")) 47 i = j - 1 48 goto instrsEmitted 49 } 50 } 51 } 52 inline = false 53 _, isV = instrs[i].(ssa.Value) 54 if isV { 55 if LanguageList[comp.TargetLang].CanInline(instrs[i]) { 56 inline = true 57 } 58 } 59 if inline { 60 postWrite := "" 61 preBuffLen := LanguageList[comp.TargetLang].buffer.Len() 62 comp.emitInstruction(instrs[i], instrs[i].Operands(make([]*ssa.Value, 0))) 63 raw := strings.TrimSpace(string(LanguageList[comp.TargetLang].buffer.Bytes()[preBuffLen:])) 64 for _, ignorePrefix := range LanguageList[comp.TargetLang].IgnorePrefixes { 65 if strings.HasPrefix(raw, ignorePrefix) { 66 sph := strings.SplitAfterN(raw, LanguageList[comp.TargetLang].StatementTerminator, 2) 67 if len(sph) != 2 { 68 panic("code to ignore not as expected: " + raw) 69 } 70 postWrite = sph[0] 71 raw = strings.TrimSpace(sph[1]) 72 break 73 } 74 } 75 bits := strings.SplitAfter(raw, LanguageList[comp.TargetLang].LineCommentMark) //comment marker must not be in strings 76 code := strings.TrimSuffix( 77 strings.TrimSpace(strings.TrimSuffix(bits[0], 78 LanguageList[comp.TargetLang].LineCommentMark)), LanguageList[comp.TargetLang].StatementTerminator) // usually a semi-colon 79 parts := strings.SplitAfterN(code, "=", 2) 80 if len(parts) != 2 { 81 panic("no = after register name in: " + code) 82 } 83 parts[0] = strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(parts[0]), "=")) 84 //println("DEBUG inlineMap[" + parts[0] + "]=" + parts[1]) 85 found := 0 86 for _, v := range comp.inlineMap { 87 if v == parts[1] { 88 found++ 89 } 90 } 91 comp.inlineMap[parts[0]] = parts[1] 92 LanguageList[comp.TargetLang].buffer.Truncate(preBuffLen) 93 fmt.Fprintln(&LanguageList[comp.TargetLang].buffer, "//[ PEEPHOLE INLINE "+parts[0]+" ] "+instrs[i].String()) 94 if postWrite != "" { 95 fmt.Fprintln(&LanguageList[comp.TargetLang].buffer, postWrite) 96 } 97 if found > 0 { 98 fmt.Fprintf(&LanguageList[comp.TargetLang].buffer, 99 "// DEBUG %d duplicate(s) found for %s\n", found, parts[1]) // this optimisation TODO 100 } 101 } else { 102 comp.emitInstruction(instrs[i], instrs[i].Operands(make([]*ssa.Value, 0))) 103 } 104 instrsEmitted: 105 } 106 //println("DEBUG new inlineMap") 107 comp.newInlineMap() // needed here too to stop these temp values bleeding to elsewhere 108 } 109 110 func (comp *Compilation) peepholeFindOpt(instrs []ssa.Instruction) (optName, regName string) { 111 switch instrs[0].(type) { 112 case *ssa.IndexAddr, *ssa.FieldAddr: 113 ptrChainSize := 1 114 if len(instrs) < 2 { 115 return // fail 116 } 117 //fmt.Println("DEBUG looking for ptrChain num refs=", len(*(instrs[0].(ssa.Value).Referrers()))) 118 if len(*(instrs[0].(ssa.Value).Referrers())) == 0 || !addrInstrUsesPointer(instrs[0]) { 119 goto nextOpts 120 } 121 //fmt.Println("DEBUG instr 0: ", instrs[0].String()) 122 for ; ptrChainSize < len(instrs); ptrChainSize++ { 123 //fmt.Println("DEBUG instr ", ptrChainSize, instrs[ptrChainSize].String()) 124 switch instrs[ptrChainSize].(type) { 125 case *ssa.IndexAddr, *ssa.FieldAddr: 126 if !addrInstrUsesPointer(instrs[ptrChainSize]) { 127 goto nextOpts 128 } 129 /* 130 fmt.Println("DEBUG i, refs, prev, this, instr(prev), instr(this)=", 131 ptrChainSize, 132 len(*instrs[ptrChainSize].(ssa.Value).Referrers()), 133 RegisterName(instrs[ptrChainSize-1].(ssa.Value)), 134 "_"+(*instrs[ptrChainSize].Operands(nil)[0]).Name(), 135 RegisterName(instrs[ptrChainSize-1].(ssa.Value))+"="+instrs[ptrChainSize-1].String(), 136 RegisterName(instrs[ptrChainSize].(ssa.Value))+"="+instrs[ptrChainSize].String()) 137 */ 138 if len(*instrs[ptrChainSize-1].(ssa.Value).Referrers()) != 1 || 139 "_"+(*instrs[ptrChainSize].Operands(nil)[0]).Name() != comp.RegisterName(instrs[ptrChainSize-1].(ssa.Value)) { 140 goto nextOpts 141 } 142 default: 143 goto nextOpts 144 } 145 } 146 if ptrChainSize > 1 { 147 /* 148 fmt.Println("DEBUG pointer chain found") 149 for i := 0; i < ptrChainSize; i++ { 150 fmt.Println("DEBUG pointer chain ", i, len(*instrs[i].(ssa.Value).Referrers()), 151 RegisterName(instrs[i].(ssa.Value))+"="+instrs[i].String()) 152 } 153 */ 154 return "pointerChain", comp.RegisterName(instrs[ptrChainSize-1].(ssa.Value)) 155 } 156 nextOpts: 157 return // fail 158 159 case *ssa.UnOp: 160 if len(instrs) < 2 { 161 return // fail 162 } 163 if instrs[0].(*ssa.UnOp).Op == token.MUL && 164 len(*instrs[0].(*ssa.UnOp).Referrers()) == 1 { 165 switch instrs[len(instrs)-1].(type) { 166 case *ssa.Index, *ssa.Field: 167 // candidate to remove load_object 168 if len(instrs) == 2 { 169 // we are at the first two in the load_object(UnOp*)+Index/Field sequence 170 if instrs[0].(*ssa.UnOp).Name() == indexOrFieldXName(instrs[1]) && 171 indexOrFieldRefCount(instrs[1]) > 0 { 172 optName = "loadObject" 173 regName = comp.RegisterName(instrs[1].(ssa.Value)) 174 return // success 175 } 176 return // fail 177 } 178 } 179 } 180 181 case *ssa.Phi: 182 if len(instrs) == 0 { 183 return // fail 184 } 185 for _, instr := range instrs { 186 _ /*phi*/, ok := instr.(*ssa.Phi) 187 if !ok { 188 return // fail 189 } 190 //if len(*phi.Referrers()) == 0 { 191 // return // fail 192 //} 193 } 194 optName = "phiList" 195 //regName is unused 196 return //success 197 198 } 199 return // fail 200 } 201 202 func addrInstrUsesPointer(i ssa.Instruction) bool { 203 switch i.(type) { 204 case *ssa.IndexAddr: 205 _, ok := i.(*ssa.IndexAddr).X.Type().Underlying().(*types.Pointer) 206 return ok 207 case *ssa.FieldAddr: 208 _, ok := i.(*ssa.FieldAddr).X.Type().Underlying().(*types.Pointer) 209 return ok 210 default: 211 return false 212 } 213 } 214 215 func indexOrFieldXName(i ssa.Instruction) string { 216 switch i.(type) { 217 case *ssa.Index: 218 return i.(*ssa.Index).X.Name() 219 case *ssa.Field: 220 return i.(*ssa.Field).X.Name() 221 default: 222 return "" 223 } 224 } 225 226 func indexOrFieldRefCount(i ssa.Instruction) int { 227 switch i.(type) { 228 case *ssa.Index: 229 return len(*i.(*ssa.Index).Referrers()) 230 case *ssa.Field: 231 return len(*i.(*ssa.Field).Referrers()) 232 default: 233 return 0 234 } 235 }