Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1 | // Derived from Inferno utils/6c/gc.h |
| 2 | // http://code.google.com/p/inferno-os/source/browse/utils/6c/gc.h |
| 3 | // |
| 4 | // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. |
| 5 | // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) |
| 6 | // Portions Copyright © 1997-1999 Vita Nuova Limited |
| 7 | // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) |
| 8 | // Portions Copyright © 2004,2006 Bruce Ellis |
| 9 | // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) |
| 10 | // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others |
| 11 | // Portions Copyright © 2009 The Go Authors. All rights reserved. |
| 12 | // |
| 13 | // Permission is hereby granted, free of charge, to any person obtaining a copy |
| 14 | // of this software and associated documentation files (the "Software"), to deal |
| 15 | // in the Software without restriction, including without limitation the rights |
| 16 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 17 | // copies of the Software, and to permit persons to whom the Software is |
| 18 | // furnished to do so, subject to the following conditions: |
| 19 | // |
| 20 | // The above copyright notice and this permission notice shall be included in |
| 21 | // all copies or substantial portions of the Software. |
| 22 | // |
| 23 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 24 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 25 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 26 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 27 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 28 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 29 | // THE SOFTWARE. |
| 30 | |
Russ Cox | d47fe80 | 2015-03-09 15:34:06 -0400 | [diff] [blame^] | 31 | // "Portable" optimizations. |
| 32 | |
| 33 | package gc |
| 34 | |
| 35 | import ( |
| 36 | "cmd/internal/obj" |
| 37 | "fmt" |
| 38 | "sort" |
| 39 | "strings" |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 40 | ) |
| 41 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 42 | type OptStats struct { |
| 43 | Ncvtreg int32 |
| 44 | Nspill int32 |
| 45 | Nreload int32 |
| 46 | Ndelmov int32 |
| 47 | Nvar int32 |
| 48 | Naddr int32 |
| 49 | } |
| 50 | |
| 51 | var Ostats OptStats |
| 52 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 53 | var noreturn_symlist [10]*Sym |
| 54 | |
Keith Randall | cd5b144 | 2015-03-11 12:58:47 -0700 | [diff] [blame] | 55 | // p is a call instruction. Does the call fail to return? |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 56 | func Noreturn(p *obj.Prog) bool { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 57 | if noreturn_symlist[0] == nil { |
| 58 | noreturn_symlist[0] = Pkglookup("panicindex", Runtimepkg) |
| 59 | noreturn_symlist[1] = Pkglookup("panicslice", Runtimepkg) |
| 60 | noreturn_symlist[2] = Pkglookup("throwinit", Runtimepkg) |
| 61 | noreturn_symlist[3] = Pkglookup("gopanic", Runtimepkg) |
| 62 | noreturn_symlist[4] = Pkglookup("panicwrap", Runtimepkg) |
| 63 | noreturn_symlist[5] = Pkglookup("throwreturn", Runtimepkg) |
| 64 | noreturn_symlist[6] = Pkglookup("selectgo", Runtimepkg) |
| 65 | noreturn_symlist[7] = Pkglookup("block", Runtimepkg) |
| 66 | } |
| 67 | |
| 68 | if p.To.Node == nil { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 69 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 70 | } |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 71 | s := ((p.To.Node).(*Node)).Sym |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 72 | if s == nil { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 73 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 74 | } |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 75 | for i := 0; noreturn_symlist[i] != nil; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 76 | if s == noreturn_symlist[i] { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 77 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 78 | } |
| 79 | } |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 80 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 81 | } |
| 82 | |
| 83 | // JMP chasing and removal. |
| 84 | // |
| 85 | // The code generator depends on being able to write out jump |
| 86 | // instructions that it can jump to now but fill in later. |
| 87 | // the linker will resolve them nicely, but they make the code |
| 88 | // longer and more difficult to follow during debugging. |
| 89 | // Remove them. |
| 90 | |
| 91 | /* what instruction does a JMP to p eventually land on? */ |
| 92 | func chasejmp(p *obj.Prog, jmploop *int) *obj.Prog { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 93 | n := 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 94 | for p != nil && p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH { |
| 95 | n++ |
| 96 | if n > 10 { |
| 97 | *jmploop = 1 |
| 98 | break |
| 99 | } |
| 100 | |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 101 | p = p.To.Val.(*obj.Prog) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 102 | } |
| 103 | |
| 104 | return p |
| 105 | } |
| 106 | |
| 107 | /* |
| 108 | * reuse reg pointer for mark/sweep state. |
| 109 | * leave reg==nil at end because alive==nil. |
| 110 | */ |
| 111 | var alive interface{} = nil |
| 112 | var dead interface{} = 1 |
| 113 | |
| 114 | /* mark all code reachable from firstp as alive */ |
| 115 | func mark(firstp *obj.Prog) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 116 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 117 | if p.Opt != dead { |
| 118 | break |
| 119 | } |
| 120 | p.Opt = alive |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 121 | if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil { |
| 122 | mark(p.To.Val.(*obj.Prog)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 123 | } |
| 124 | if p.As == obj.AJMP || p.As == obj.ARET || p.As == obj.AUNDEF { |
| 125 | break |
| 126 | } |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | func fixjmp(firstp *obj.Prog) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 131 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 132 | fmt.Printf("\nfixjmp\n") |
| 133 | } |
| 134 | |
| 135 | // pass 1: resolve jump to jump, mark all code as dead. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 136 | jmploop := 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 137 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 138 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 139 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 140 | fmt.Printf("%v\n", p) |
| 141 | } |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 142 | if p.As != obj.ACALL && p.To.Type == obj.TYPE_BRANCH && p.To.Val.(*obj.Prog) != nil && p.To.Val.(*obj.Prog).As == obj.AJMP { |
| 143 | p.To.Val = chasejmp(p.To.Val.(*obj.Prog), &jmploop) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 144 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 145 | fmt.Printf("->%v\n", p) |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | p.Opt = dead |
| 150 | } |
| 151 | |
| 152 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 153 | fmt.Printf("\n") |
| 154 | } |
| 155 | |
| 156 | // pass 2: mark all reachable code alive |
| 157 | mark(firstp) |
| 158 | |
| 159 | // pass 3: delete dead code (mostly JMPs). |
Russ Cox | 175929b | 2015-03-02 14:22:05 -0500 | [diff] [blame] | 160 | var last *obj.Prog |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 161 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 162 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 163 | if p.Opt == dead { |
| 164 | if p.Link == nil && p.As == obj.ARET && last != nil && last.As != obj.ARET { |
| 165 | // This is the final ARET, and the code so far doesn't have one. |
| 166 | // Let it stay. The register allocator assumes that all live code in |
| 167 | // the function can be traversed by starting at all the RET instructions |
| 168 | // and following predecessor links. If we remove the final RET, |
| 169 | // this assumption will not hold in the case of an infinite loop |
| 170 | // at the end of a function. |
| 171 | // Keep the RET but mark it dead for the liveness analysis. |
| 172 | p.Mode = 1 |
| 173 | } else { |
| 174 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 175 | fmt.Printf("del %v\n", p) |
| 176 | } |
| 177 | continue |
| 178 | } |
| 179 | } |
| 180 | |
| 181 | if last != nil { |
| 182 | last.Link = p |
| 183 | } |
| 184 | last = p |
| 185 | } |
| 186 | |
| 187 | last.Link = nil |
| 188 | |
| 189 | // pass 4: elide JMP to next instruction. |
| 190 | // only safe if there are no jumps to JMPs anymore. |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 191 | if jmploop == 0 { |
Russ Cox | 175929b | 2015-03-02 14:22:05 -0500 | [diff] [blame] | 192 | var last *obj.Prog |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 193 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 194 | if p.As == obj.AJMP && p.To.Type == obj.TYPE_BRANCH && p.To.Val == p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 195 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 196 | fmt.Printf("del %v\n", p) |
| 197 | } |
| 198 | continue |
| 199 | } |
| 200 | |
| 201 | if last != nil { |
| 202 | last.Link = p |
| 203 | } |
| 204 | last = p |
| 205 | } |
| 206 | |
| 207 | last.Link = nil |
| 208 | } |
| 209 | |
| 210 | if Debug['R'] != 0 && Debug['v'] != 0 { |
| 211 | fmt.Printf("\n") |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 212 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 213 | fmt.Printf("%v\n", p) |
| 214 | } |
| 215 | fmt.Printf("\n") |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | // Control flow analysis. The Flow structures hold predecessor and successor |
| 220 | // information as well as basic loop analysis. |
| 221 | // |
| 222 | // graph = flowstart(firstp, 0); |
| 223 | // ... use flow graph ... |
| 224 | // flowend(graph); // free graph |
| 225 | // |
| 226 | // Typical uses of the flow graph are to iterate over all the flow-relevant instructions: |
| 227 | // |
| 228 | // for(f = graph->start; f != nil; f = f->link) |
| 229 | // |
| 230 | // or, given an instruction f, to iterate over all the predecessors, which is |
| 231 | // f->p1 and this list: |
| 232 | // |
| 233 | // for(f2 = f->p2; f2 != nil; f2 = f2->p2link) |
| 234 | // |
| 235 | // The size argument to flowstart specifies an amount of zeroed memory |
| 236 | // to allocate in every f->data field, for use by the client. |
| 237 | // If size == 0, f->data will be nil. |
| 238 | |
Russ Cox | 4bbd7ae | 2015-03-02 15:22:19 -0500 | [diff] [blame] | 239 | var flowmark int |
| 240 | |
Russ Cox | d47fe80 | 2015-03-09 15:34:06 -0400 | [diff] [blame^] | 241 | // MaxFlowProg is the maximum size program (counted in instructions) |
| 242 | // for which the flow code will build a graph. Functions larger than this limit |
| 243 | // will not have flow graphs and consequently will not be optimized. |
| 244 | const MaxFlowProg = 50000 |
| 245 | |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 246 | func Flowstart(firstp *obj.Prog, newData func() interface{}) *Graph { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 247 | // Count and mark instructions to annotate. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 248 | nf := 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 249 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 250 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 251 | p.Opt = nil // should be already, but just in case |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 252 | Thearch.Proginfo(p) |
| 253 | if p.Info.Flags&Skip != 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 254 | continue |
| 255 | } |
Russ Cox | 4bbd7ae | 2015-03-02 15:22:19 -0500 | [diff] [blame] | 256 | p.Opt = &flowmark |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 257 | nf++ |
| 258 | } |
| 259 | |
| 260 | if nf == 0 { |
| 261 | return nil |
| 262 | } |
| 263 | |
Russ Cox | d47fe80 | 2015-03-09 15:34:06 -0400 | [diff] [blame^] | 264 | if nf >= MaxFlowProg { |
| 265 | if Debug['v'] != 0 { |
| 266 | Warn("%v is too big (%d instructions)", Sconv(Curfn.Nname.Sym, 0), nf) |
| 267 | } |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 268 | return nil |
| 269 | } |
| 270 | |
| 271 | // Allocate annotations and assign to instructions. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 272 | graph := new(Graph) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 273 | ff := make([]Flow, nf) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 274 | start := &ff[0] |
| 275 | id := 0 |
| 276 | var last *Flow |
| 277 | for p := firstp; p != nil; p = p.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 278 | if p.Opt == nil { |
| 279 | continue |
| 280 | } |
| 281 | f := &ff[0] |
| 282 | ff = ff[1:] |
| 283 | p.Opt = f |
| 284 | f.Prog = p |
| 285 | if last != nil { |
| 286 | last.Link = f |
| 287 | } |
| 288 | last = f |
| 289 | if newData != nil { |
| 290 | f.Data = newData() |
| 291 | } |
| 292 | f.Id = int32(id) |
| 293 | id++ |
| 294 | } |
| 295 | |
| 296 | // Fill in pred/succ information. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 297 | var f1 *Flow |
| 298 | var p *obj.Prog |
| 299 | for f := start; f != nil; f = f.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 300 | p = f.Prog |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 301 | if p.Info.Flags&Break == 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 302 | f1 = f.Link |
| 303 | f.S1 = f1 |
| 304 | f1.P1 = f |
| 305 | } |
| 306 | |
| 307 | if p.To.Type == obj.TYPE_BRANCH { |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 308 | if p.To.Val == nil { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 309 | Fatal("pnil %v", p) |
| 310 | } |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 311 | f1 = p.To.Val.(*obj.Prog).Opt.(*Flow) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 312 | if f1 == nil { |
Russ Cox | 532ccae | 2015-03-16 15:54:44 -0400 | [diff] [blame] | 313 | Fatal("fnil %v / %v", p, p.To.Val.(*obj.Prog)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 314 | } |
| 315 | if f1 == f { |
| 316 | //fatal("self loop %P", p); |
| 317 | continue |
| 318 | } |
| 319 | |
| 320 | f.S2 = f1 |
| 321 | f.P2link = f1.P2 |
| 322 | f1.P2 = f |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | graph.Start = start |
| 327 | graph.Num = nf |
| 328 | return graph |
| 329 | } |
| 330 | |
| 331 | func Flowend(graph *Graph) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 332 | for f := graph.Start; f != nil; f = f.Link { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 333 | f.Prog.Info.Flags = 0 // drop cached proginfo |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 334 | f.Prog.Opt = nil |
| 335 | } |
| 336 | } |
| 337 | |
| 338 | /* |
| 339 | * find looping structure |
| 340 | * |
| 341 | * 1) find reverse postordering |
| 342 | * 2) find approximate dominators, |
| 343 | * the actual dominators if the flow graph is reducible |
| 344 | * otherwise, dominators plus some other non-dominators. |
| 345 | * See Matthew S. Hecht and Jeffrey D. Ullman, |
| 346 | * "Analysis of a Simple Algorithm for Global Data Flow Problems", |
| 347 | * Conf. Record of ACM Symp. on Principles of Prog. Langs, Boston, Massachusetts, |
| 348 | * Oct. 1-3, 1973, pp. 207-217. |
| 349 | * 3) find all nodes with a predecessor dominated by the current node. |
| 350 | * such a node is a loop head. |
| 351 | * recursively, all preds with a greater rpo number are in the loop |
| 352 | */ |
| 353 | func postorder(r *Flow, rpo2r []*Flow, n int32) int32 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 354 | r.Rpo = 1 |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 355 | r1 := r.S1 |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 356 | if r1 != nil && r1.Rpo == 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 357 | n = postorder(r1, rpo2r, n) |
| 358 | } |
| 359 | r1 = r.S2 |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 360 | if r1 != nil && r1.Rpo == 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 361 | n = postorder(r1, rpo2r, n) |
| 362 | } |
| 363 | rpo2r[n] = r |
| 364 | n++ |
| 365 | return n |
| 366 | } |
| 367 | |
| 368 | func rpolca(idom []int32, rpo1 int32, rpo2 int32) int32 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 369 | if rpo1 == -1 { |
| 370 | return rpo2 |
| 371 | } |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 372 | var t int32 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 373 | for rpo1 != rpo2 { |
| 374 | if rpo1 > rpo2 { |
| 375 | t = rpo2 |
| 376 | rpo2 = rpo1 |
| 377 | rpo1 = t |
| 378 | } |
| 379 | |
| 380 | for rpo1 < rpo2 { |
| 381 | t = idom[rpo2] |
| 382 | if t >= rpo2 { |
| 383 | Fatal("bad idom") |
| 384 | } |
| 385 | rpo2 = t |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | return rpo1 |
| 390 | } |
| 391 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 392 | func doms(idom []int32, r int32, s int32) bool { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 393 | for s > r { |
| 394 | s = idom[s] |
| 395 | } |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 396 | return s == r |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 397 | } |
| 398 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 399 | func loophead(idom []int32, r *Flow) bool { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 400 | src := r.Rpo |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 401 | if r.P1 != nil && doms(idom, src, r.P1.Rpo) { |
| 402 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 403 | } |
| 404 | for r = r.P2; r != nil; r = r.P2link { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 405 | if doms(idom, src, r.Rpo) { |
| 406 | return true |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 407 | } |
| 408 | } |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 409 | return false |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 410 | } |
| 411 | |
| 412 | func loopmark(rpo2r **Flow, head int32, r *Flow) { |
| 413 | if r.Rpo < head || r.Active == head { |
| 414 | return |
| 415 | } |
| 416 | r.Active = head |
| 417 | r.Loop += LOOP |
| 418 | if r.P1 != nil { |
| 419 | loopmark(rpo2r, head, r.P1) |
| 420 | } |
| 421 | for r = r.P2; r != nil; r = r.P2link { |
| 422 | loopmark(rpo2r, head, r) |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | func flowrpo(g *Graph) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 427 | g.Rpo = make([]*Flow, g.Num) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 428 | idom := make([]int32, g.Num) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 429 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 430 | for r1 := g.Start; r1 != nil; r1 = r1.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 431 | r1.Active = 0 |
| 432 | } |
| 433 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 434 | rpo2r := g.Rpo |
| 435 | d := postorder(g.Start, rpo2r, 0) |
| 436 | nr := int32(g.Num) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 437 | if d > nr { |
| 438 | Fatal("too many reg nodes %d %d", d, nr) |
| 439 | } |
| 440 | nr = d |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 441 | var r1 *Flow |
| 442 | for i := int32(0); i < nr/2; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 443 | r1 = rpo2r[i] |
| 444 | rpo2r[i] = rpo2r[nr-1-i] |
| 445 | rpo2r[nr-1-i] = r1 |
| 446 | } |
| 447 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 448 | for i := int32(0); i < nr; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 449 | rpo2r[i].Rpo = i |
| 450 | } |
| 451 | |
| 452 | idom[0] = 0 |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 453 | var me int32 |
| 454 | for i := int32(0); i < nr; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 455 | r1 = rpo2r[i] |
| 456 | me = r1.Rpo |
| 457 | d = -1 |
| 458 | |
| 459 | // rpo2r[r->rpo] == r protects against considering dead code, |
| 460 | // which has r->rpo == 0. |
| 461 | if r1.P1 != nil && rpo2r[r1.P1.Rpo] == r1.P1 && r1.P1.Rpo < me { |
| 462 | d = r1.P1.Rpo |
| 463 | } |
| 464 | for r1 = r1.P2; r1 != nil; r1 = r1.P2link { |
| 465 | if rpo2r[r1.Rpo] == r1 && r1.Rpo < me { |
| 466 | d = rpolca(idom, d, r1.Rpo) |
| 467 | } |
| 468 | } |
| 469 | idom[i] = d |
| 470 | } |
| 471 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 472 | for i := int32(0); i < nr; i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 473 | r1 = rpo2r[i] |
| 474 | r1.Loop++ |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 475 | if r1.P2 != nil && loophead(idom, r1) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 476 | loopmark(&rpo2r[0], i, r1) |
| 477 | } |
| 478 | } |
| 479 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 480 | for r1 := g.Start; r1 != nil; r1 = r1.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 481 | r1.Active = 0 |
| 482 | } |
| 483 | } |
| 484 | |
| 485 | func Uniqp(r *Flow) *Flow { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 486 | r1 := r.P1 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 487 | if r1 == nil { |
| 488 | r1 = r.P2 |
| 489 | if r1 == nil || r1.P2link != nil { |
| 490 | return nil |
| 491 | } |
| 492 | } else if r.P2 != nil { |
| 493 | return nil |
| 494 | } |
| 495 | return r1 |
| 496 | } |
| 497 | |
| 498 | func Uniqs(r *Flow) *Flow { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 499 | r1 := r.S1 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 500 | if r1 == nil { |
| 501 | r1 = r.S2 |
| 502 | if r1 == nil { |
| 503 | return nil |
| 504 | } |
| 505 | } else if r.S2 != nil { |
| 506 | return nil |
| 507 | } |
| 508 | return r1 |
| 509 | } |
| 510 | |
| 511 | // The compilers assume they can generate temporary variables |
| 512 | // as needed to preserve the right semantics or simplify code |
| 513 | // generation and the back end will still generate good code. |
| 514 | // This results in a large number of ephemeral temporary variables. |
| 515 | // Merge temps with non-overlapping lifetimes and equal types using the |
| 516 | // greedy algorithm in Poletto and Sarkar, "Linear Scan Register Allocation", |
| 517 | // ACM TOPLAS 1999. |
| 518 | |
| 519 | type TempVar struct { |
| 520 | node *Node |
Russ Cox | cdb7d7d | 2015-03-05 13:57:36 -0500 | [diff] [blame] | 521 | def *Flow // definition of temp var |
| 522 | use *Flow // use list, chained through Flow.data |
| 523 | freelink *TempVar // next free temp in Type.opt list |
| 524 | merge *TempVar // merge var with this one |
| 525 | start int64 // smallest Prog.pc in live range |
| 526 | end int64 // largest Prog.pc in live range |
| 527 | addr uint8 // address taken - no accurate end |
| 528 | removed uint8 // removed from program |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 529 | } |
| 530 | |
| 531 | type startcmp []*TempVar |
| 532 | |
| 533 | func (x startcmp) Len() int { |
| 534 | return len(x) |
| 535 | } |
| 536 | |
| 537 | func (x startcmp) Swap(i, j int) { |
| 538 | x[i], x[j] = x[j], x[i] |
| 539 | } |
| 540 | |
| 541 | func (x startcmp) Less(i, j int) bool { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 542 | a := x[i] |
| 543 | b := x[j] |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 544 | |
| 545 | if a.start < b.start { |
| 546 | return true |
| 547 | } |
| 548 | if a.start > b.start { |
| 549 | return false |
| 550 | } |
| 551 | |
| 552 | // Order what's left by id or symbol name, |
| 553 | // just so that sort is forced into a specific ordering, |
| 554 | // so that the result of the sort does not depend on |
| 555 | // the sort implementation. |
| 556 | if a.def != b.def { |
| 557 | return int(a.def.Id-b.def.Id) < 0 |
| 558 | } |
| 559 | if a.node != b.node { |
| 560 | return stringsCompare(a.node.Sym.Name, b.node.Sym.Name) < 0 |
| 561 | } |
| 562 | return false |
| 563 | } |
| 564 | |
| 565 | // Is n available for merging? |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 566 | func canmerge(n *Node) bool { |
| 567 | return n.Class == PAUTO && strings.HasPrefix(n.Sym.Name, "autotmp") |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 568 | } |
| 569 | |
| 570 | func mergetemp(firstp *obj.Prog) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 571 | const ( |
Russ Cox | d47fe80 | 2015-03-09 15:34:06 -0400 | [diff] [blame^] | 572 | debugmerge = 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 573 | ) |
| 574 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 575 | g := Flowstart(firstp, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 576 | if g == nil { |
| 577 | return |
| 578 | } |
| 579 | |
| 580 | // Build list of all mergeable variables. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 581 | nvar := 0 |
| 582 | for l := Curfn.Dcl; l != nil; l = l.Next { |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 583 | if canmerge(l.N) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 584 | nvar++ |
| 585 | } |
| 586 | } |
| 587 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 588 | var_ := make([]TempVar, nvar) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 589 | nvar = 0 |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 590 | var n *Node |
| 591 | var v *TempVar |
| 592 | for l := Curfn.Dcl; l != nil; l = l.Next { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 593 | n = l.N |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 594 | if canmerge(n) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 595 | v = &var_[nvar] |
| 596 | nvar++ |
| 597 | n.Opt = v |
| 598 | v.node = n |
| 599 | } |
| 600 | } |
| 601 | |
| 602 | // Build list of uses. |
| 603 | // We assume that the earliest reference to a temporary is its definition. |
| 604 | // This is not true of variables in general but our temporaries are all |
| 605 | // single-use (that's why we have so many!). |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 606 | for f := g.Start; f != nil; f = f.Link { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 607 | p := f.Prog |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 608 | if p.From.Node != nil && ((p.From.Node).(*Node)).Opt != nil && p.To.Node != nil && ((p.To.Node).(*Node)).Opt != nil { |
| 609 | Fatal("double node %v", p) |
| 610 | } |
| 611 | v = nil |
| 612 | n, _ = p.From.Node.(*Node) |
| 613 | if n != nil { |
| 614 | v, _ = n.Opt.(*TempVar) |
| 615 | } |
| 616 | if v == nil { |
| 617 | n, _ = p.To.Node.(*Node) |
| 618 | if n != nil { |
| 619 | v, _ = n.Opt.(*TempVar) |
| 620 | } |
| 621 | } |
| 622 | if v != nil { |
| 623 | if v.def == nil { |
| 624 | v.def = f |
| 625 | } |
| 626 | f.Data = v.use |
| 627 | v.use = f |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 628 | if n == p.From.Node && (p.Info.Flags&LeftAddr != 0) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 629 | v.addr = 1 |
| 630 | } |
| 631 | } |
| 632 | } |
| 633 | |
| 634 | if debugmerge > 1 && Debug['v'] != 0 { |
| 635 | Dumpit("before", g.Start, 0) |
| 636 | } |
| 637 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 638 | nkill := 0 |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 639 | |
| 640 | // Special case. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 641 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 642 | v = &var_[i] |
| 643 | if v.addr != 0 { |
| 644 | continue |
| 645 | } |
| 646 | |
| 647 | // Used in only one instruction, which had better be a write. |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 648 | f := v.use |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 649 | if f != nil && f.Data.(*Flow) == nil { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 650 | p := f.Prog |
| 651 | if p.To.Node == v.node && (p.Info.Flags&RightWrite != 0) && p.Info.Flags&RightRead == 0 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 652 | p.As = obj.ANOP |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 653 | p.To = obj.Addr{} |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 654 | v.removed = 1 |
| 655 | if debugmerge > 0 && Debug['v'] != 0 { |
| 656 | fmt.Printf("drop write-only %v\n", Sconv(v.node.Sym, 0)) |
| 657 | } |
| 658 | } else { |
| 659 | Fatal("temp used and not set: %v", p) |
| 660 | } |
| 661 | nkill++ |
| 662 | continue |
| 663 | } |
| 664 | |
| 665 | // Written in one instruction, read in the next, otherwise unused, |
| 666 | // no jumps to the next instruction. Happens mainly in 386 compiler. |
| 667 | f = v.use |
| 668 | if f != nil && f.Link == f.Data.(*Flow) && (f.Data.(*Flow)).Data.(*Flow) == nil && Uniqp(f.Link) == f { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 669 | p := f.Prog |
| 670 | p1 := f.Link.Prog |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 671 | const ( |
| 672 | SizeAny = SizeB | SizeW | SizeL | SizeQ | SizeF | SizeD |
| 673 | ) |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 674 | if p.From.Node == v.node && p1.To.Node == v.node && (p.Info.Flags&Move != 0) && (p.Info.Flags|p1.Info.Flags)&(LeftAddr|RightAddr) == 0 && p.Info.Flags&SizeAny == p1.Info.Flags&SizeAny { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 675 | p1.From = p.From |
| 676 | Thearch.Excise(f) |
| 677 | v.removed = 1 |
| 678 | if debugmerge > 0 && Debug['v'] != 0 { |
| 679 | fmt.Printf("drop immediate-use %v\n", Sconv(v.node.Sym, 0)) |
| 680 | } |
| 681 | } |
| 682 | |
| 683 | nkill++ |
| 684 | continue |
| 685 | } |
| 686 | } |
| 687 | |
| 688 | // Traverse live range of each variable to set start, end. |
| 689 | // Each flood uses a new value of gen so that we don't have |
| 690 | // to clear all the r->active words after each variable. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 691 | gen := int32(0) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 692 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 693 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 694 | v = &var_[i] |
| 695 | gen++ |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 696 | for f := v.use; f != nil; f = f.Data.(*Flow) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 697 | mergewalk(v, f, uint32(gen)) |
| 698 | } |
| 699 | if v.addr != 0 { |
| 700 | gen++ |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 701 | for f := v.use; f != nil; f = f.Data.(*Flow) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 702 | varkillwalk(v, f, uint32(gen)) |
| 703 | } |
| 704 | } |
| 705 | } |
| 706 | |
| 707 | // Sort variables by start. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 708 | bystart := make([]*TempVar, len(var_)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 709 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 710 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 711 | bystart[i] = &var_[i] |
| 712 | } |
| 713 | sort.Sort(startcmp(bystart[:len(var_)])) |
| 714 | |
| 715 | // List of in-use variables, sorted by end, so that the ones that |
| 716 | // will last the longest are the earliest ones in the array. |
| 717 | // The tail inuse[nfree:] holds no-longer-used variables. |
| 718 | // In theory we should use a sorted tree so that insertions are |
| 719 | // guaranteed O(log n) and then the loop is guaranteed O(n log n). |
| 720 | // In practice, it doesn't really matter. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 721 | inuse := make([]*TempVar, len(var_)) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 722 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 723 | ninuse := 0 |
| 724 | nfree := len(var_) |
| 725 | var t *Type |
| 726 | var v1 *TempVar |
| 727 | var j int |
| 728 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 729 | v = bystart[i] |
| 730 | if debugmerge > 0 && Debug['v'] != 0 { |
| 731 | fmt.Printf("consider %v: removed=%d\n", Nconv(v.node, obj.FmtSharp), v.removed) |
| 732 | } |
| 733 | |
| 734 | if v.removed != 0 { |
| 735 | continue |
| 736 | } |
| 737 | |
| 738 | // Expire no longer in use. |
| 739 | for ninuse > 0 && inuse[ninuse-1].end < v.start { |
| 740 | ninuse-- |
| 741 | v1 = inuse[ninuse] |
| 742 | nfree-- |
| 743 | inuse[nfree] = v1 |
| 744 | } |
| 745 | |
| 746 | if debugmerge > 0 && Debug['v'] != 0 { |
| 747 | fmt.Printf("consider %v: removed=%d nfree=%d nvar=%d\n", Nconv(v.node, obj.FmtSharp), v.removed, nfree, len(var_)) |
| 748 | } |
| 749 | |
| 750 | // Find old temp to reuse if possible. |
| 751 | t = v.node.Type |
| 752 | |
| 753 | for j = nfree; j < len(var_); j++ { |
| 754 | v1 = inuse[j] |
| 755 | if debugmerge > 0 && Debug['v'] != 0 { |
Dave Cheney | b006d38 | 2015-03-06 18:42:58 +1100 | [diff] [blame] | 756 | fmt.Printf("consider %v: maybe %v: type=%v,%v addrtaken=%v,%v\n", Nconv(v.node, obj.FmtSharp), Nconv(v1.node, obj.FmtSharp), Tconv(t, 0), Tconv(v1.node.Type, 0), v.node.Addrtaken, v1.node.Addrtaken) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 757 | } |
| 758 | |
| 759 | // Require the types to match but also require the addrtaken bits to match. |
| 760 | // If a variable's address is taken, that disables registerization for the individual |
| 761 | // words of the variable (for example, the base,len,cap of a slice). |
| 762 | // We don't want to merge a non-addressed var with an addressed one and |
| 763 | // inhibit registerization of the former. |
| 764 | if Eqtype(t, v1.node.Type) && v.node.Addrtaken == v1.node.Addrtaken { |
| 765 | inuse[j] = inuse[nfree] |
| 766 | nfree++ |
| 767 | if v1.merge != nil { |
| 768 | v.merge = v1.merge |
| 769 | } else { |
| 770 | v.merge = v1 |
| 771 | } |
| 772 | nkill++ |
| 773 | break |
| 774 | } |
| 775 | } |
| 776 | |
| 777 | // Sort v into inuse. |
| 778 | j = ninuse |
| 779 | ninuse++ |
| 780 | |
| 781 | for j > 0 && inuse[j-1].end < v.end { |
| 782 | inuse[j] = inuse[j-1] |
| 783 | j-- |
| 784 | } |
| 785 | |
| 786 | inuse[j] = v |
| 787 | } |
| 788 | |
| 789 | if debugmerge > 0 && Debug['v'] != 0 { |
| 790 | fmt.Printf("%v [%d - %d]\n", Sconv(Curfn.Nname.Sym, 0), len(var_), nkill) |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 791 | var v *TempVar |
| 792 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 793 | v = &var_[i] |
| 794 | fmt.Printf("var %v %v %d-%d", Nconv(v.node, obj.FmtSharp), Tconv(v.node.Type, 0), v.start, v.end) |
| 795 | if v.addr != 0 { |
| 796 | fmt.Printf(" addr=1") |
| 797 | } |
| 798 | if v.removed != 0 { |
| 799 | fmt.Printf(" dead=1") |
| 800 | } |
| 801 | if v.merge != nil { |
| 802 | fmt.Printf(" merge %v", Nconv(v.merge.node, obj.FmtSharp)) |
| 803 | } |
| 804 | if v.start == v.end && v.def != nil { |
| 805 | fmt.Printf(" %v", v.def.Prog) |
| 806 | } |
| 807 | fmt.Printf("\n") |
| 808 | } |
| 809 | |
| 810 | if debugmerge > 1 && Debug['v'] != 0 { |
| 811 | Dumpit("after", g.Start, 0) |
| 812 | } |
| 813 | } |
| 814 | |
| 815 | // Update node references to use merged temporaries. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 816 | for f := g.Start; f != nil; f = f.Link { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 817 | p := f.Prog |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 818 | n, _ = p.From.Node.(*Node) |
| 819 | if n != nil { |
| 820 | v, _ = n.Opt.(*TempVar) |
| 821 | if v != nil && v.merge != nil { |
| 822 | p.From.Node = v.merge.node |
| 823 | } |
| 824 | } |
| 825 | n, _ = p.To.Node.(*Node) |
| 826 | if n != nil { |
| 827 | v, _ = n.Opt.(*TempVar) |
| 828 | if v != nil && v.merge != nil { |
| 829 | p.To.Node = v.merge.node |
| 830 | } |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | // Delete merged nodes from declaration list. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 835 | var l *NodeList |
| 836 | for lp := &Curfn.Dcl; ; { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 837 | l = *lp |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 838 | if l == nil { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 839 | break |
| 840 | } |
| 841 | |
| 842 | Curfn.Dcl.End = l |
| 843 | n = l.N |
| 844 | v, _ = n.Opt.(*TempVar) |
| 845 | if v != nil && (v.merge != nil || v.removed != 0) { |
| 846 | *lp = l.Next |
| 847 | continue |
| 848 | } |
| 849 | |
| 850 | lp = &l.Next |
| 851 | } |
| 852 | |
| 853 | // Clear aux structures. |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 854 | for i := 0; i < len(var_); i++ { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 855 | var_[i].node.Opt = nil |
| 856 | } |
| 857 | |
| 858 | Flowend(g) |
| 859 | } |
| 860 | |
| 861 | func mergewalk(v *TempVar, f0 *Flow, gen uint32) { |
| 862 | var p *obj.Prog |
| 863 | var f1 *Flow |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 864 | |
| 865 | for f1 = f0; f1 != nil; f1 = f1.P1 { |
| 866 | if uint32(f1.Active) == gen { |
| 867 | break |
| 868 | } |
| 869 | f1.Active = int32(gen) |
| 870 | p = f1.Prog |
| 871 | if v.end < p.Pc { |
| 872 | v.end = p.Pc |
| 873 | } |
| 874 | if f1 == v.def { |
| 875 | v.start = p.Pc |
| 876 | break |
| 877 | } |
| 878 | } |
| 879 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 880 | var f2 *Flow |
| 881 | for f := f0; f != f1; f = f.P1 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 882 | for f2 = f.P2; f2 != nil; f2 = f2.P2link { |
| 883 | mergewalk(v, f2, gen) |
| 884 | } |
| 885 | } |
| 886 | } |
| 887 | |
| 888 | func varkillwalk(v *TempVar, f0 *Flow, gen uint32) { |
| 889 | var p *obj.Prog |
| 890 | var f1 *Flow |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 891 | |
| 892 | for f1 = f0; f1 != nil; f1 = f1.S1 { |
| 893 | if uint32(f1.Active) == gen { |
| 894 | break |
| 895 | } |
| 896 | f1.Active = int32(gen) |
| 897 | p = f1.Prog |
| 898 | if v.end < p.Pc { |
| 899 | v.end = p.Pc |
| 900 | } |
| 901 | if v.start > p.Pc { |
| 902 | v.start = p.Pc |
| 903 | } |
| 904 | if p.As == obj.ARET || (p.As == obj.AVARKILL && p.To.Node == v.node) { |
| 905 | break |
| 906 | } |
| 907 | } |
| 908 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 909 | for f := f0; f != f1; f = f.S1 { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 910 | varkillwalk(v, f.S2, gen) |
| 911 | } |
| 912 | } |
| 913 | |
| 914 | // Eliminate redundant nil pointer checks. |
| 915 | // |
| 916 | // The code generation pass emits a CHECKNIL for every possibly nil pointer. |
| 917 | // This pass removes a CHECKNIL if every predecessor path has already |
| 918 | // checked this value for nil. |
| 919 | // |
| 920 | // Simple backwards flood from check to definition. |
| 921 | // Run prog loop backward from end of program to beginning to avoid quadratic |
| 922 | // behavior removing a run of checks. |
| 923 | // |
| 924 | // Assume that stack variables with address not taken can be loaded multiple times |
| 925 | // from memory without being rechecked. Other variables need to be checked on |
| 926 | // each load. |
| 927 | type NilVar struct { |
| 928 | } |
| 929 | |
| 930 | var killed int // f->data is either nil or &killed |
| 931 | |
| 932 | func nilopt(firstp *obj.Prog) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 933 | g := Flowstart(firstp, nil) |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 934 | if g == nil { |
| 935 | return |
| 936 | } |
| 937 | |
| 938 | if Debug_checknil > 1 { /* || strcmp(curfn->nname->sym->name, "f1") == 0 */ |
| 939 | Dumpit("nilopt", g.Start, 0) |
| 940 | } |
| 941 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 942 | ncheck := 0 |
| 943 | nkill := 0 |
| 944 | var p *obj.Prog |
| 945 | for f := g.Start; f != nil; f = f.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 946 | p = f.Prog |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 947 | if p.As != obj.ACHECKNIL || !Thearch.Regtyp(&p.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 948 | continue |
| 949 | } |
| 950 | ncheck++ |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 951 | if Thearch.Stackaddr(&p.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 952 | if Debug_checknil != 0 && p.Lineno > 1 { |
| 953 | Warnl(int(p.Lineno), "removed nil check of SP address") |
| 954 | } |
| 955 | f.Data = &killed |
| 956 | continue |
| 957 | } |
| 958 | |
| 959 | nilwalkfwd(f) |
| 960 | if f.Data != nil { |
| 961 | if Debug_checknil != 0 && p.Lineno > 1 { |
| 962 | Warnl(int(p.Lineno), "removed nil check before indirect") |
| 963 | } |
| 964 | continue |
| 965 | } |
| 966 | |
| 967 | nilwalkback(f) |
| 968 | if f.Data != nil { |
| 969 | if Debug_checknil != 0 && p.Lineno > 1 { |
| 970 | Warnl(int(p.Lineno), "removed repeated nil check") |
| 971 | } |
| 972 | continue |
| 973 | } |
| 974 | } |
| 975 | |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 976 | for f := g.Start; f != nil; f = f.Link { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 977 | if f.Data != nil { |
| 978 | nkill++ |
| 979 | Thearch.Excise(f) |
| 980 | } |
| 981 | } |
| 982 | |
| 983 | Flowend(g) |
| 984 | |
| 985 | if Debug_checknil > 1 { |
| 986 | fmt.Printf("%v: removed %d of %d nil checks\n", Sconv(Curfn.Nname.Sym, 0), nkill, ncheck) |
| 987 | } |
| 988 | } |
| 989 | |
| 990 | func nilwalkback(fcheck *Flow) { |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 991 | for f := fcheck; f != nil; f = Uniqp(f) { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 992 | p := f.Prog |
| 993 | if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 994 | // Found initialization of value we're checking for nil. |
| 995 | // without first finding the check, so this one is unchecked. |
| 996 | return |
| 997 | } |
| 998 | |
Russ Cox | dc7b54b | 2015-02-17 22:13:49 -0500 | [diff] [blame] | 999 | if f != fcheck && p.As == obj.ACHECKNIL && Thearch.Sameaddr(&p.From, &fcheck.Prog.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1000 | fcheck.Data = &killed |
| 1001 | return |
| 1002 | } |
| 1003 | } |
| 1004 | } |
| 1005 | |
| 1006 | // Here is a more complex version that scans backward across branches. |
| 1007 | // It assumes fcheck->kill = 1 has been set on entry, and its job is to find a reason |
| 1008 | // to keep the check (setting fcheck->kill = 0). |
| 1009 | // It doesn't handle copying of aggregates as well as I would like, |
| 1010 | // nor variables with their address taken, |
| 1011 | // and it's too subtle to turn on this late in Go 1.2. Perhaps for Go 1.3. |
| 1012 | /* |
| 1013 | for(f1 = f0; f1 != nil; f1 = f1->p1) { |
| 1014 | if(f1->active == gen) |
| 1015 | break; |
| 1016 | f1->active = gen; |
| 1017 | p = f1->prog; |
| 1018 | |
| 1019 | // If same check, stop this loop but still check |
| 1020 | // alternate predecessors up to this point. |
| 1021 | if(f1 != fcheck && p->as == ACHECKNIL && thearch.sameaddr(&p->from, &fcheck->prog->from)) |
| 1022 | break; |
| 1023 | |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1024 | if((p.Info.flags & RightWrite) && thearch.sameaddr(&p->to, &fcheck->prog->from)) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1025 | // Found initialization of value we're checking for nil. |
| 1026 | // without first finding the check, so this one is unchecked. |
| 1027 | fcheck->kill = 0; |
| 1028 | return; |
| 1029 | } |
| 1030 | |
| 1031 | if(f1->p1 == nil && f1->p2 == nil) { |
| 1032 | print("lost pred for %P\n", fcheck->prog); |
| 1033 | for(f1=f0; f1!=nil; f1=f1->p1) { |
| 1034 | thearch.proginfo(&info, f1->prog); |
| 1035 | print("\t%P %d %d %D %D\n", r1->prog, info.flags&RightWrite, thearch.sameaddr(&f1->prog->to, &fcheck->prog->from), &f1->prog->to, &fcheck->prog->from); |
| 1036 | } |
| 1037 | fatal("lost pred trail"); |
| 1038 | } |
| 1039 | } |
| 1040 | |
| 1041 | for(f = f0; f != f1; f = f->p1) |
| 1042 | for(f2 = f->p2; f2 != nil; f2 = f2->p2link) |
| 1043 | nilwalkback(fcheck, f2, gen); |
| 1044 | */ |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1045 | |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1046 | func nilwalkfwd(fcheck *Flow) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1047 | // If the path down from rcheck dereferences the address |
| 1048 | // (possibly with a small offset) before writing to memory |
| 1049 | // and before any subsequent checks, it's okay to wait for |
| 1050 | // that implicit check. Only consider this basic block to |
| 1051 | // avoid problems like: |
| 1052 | // _ = *x // should panic |
| 1053 | // for {} // no writes but infinite loop may be considered visible |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1054 | |
Russ Cox | 175929b | 2015-03-02 14:22:05 -0500 | [diff] [blame] | 1055 | var last *Flow |
Russ Cox | 382b44e | 2015-02-23 16:07:24 -0500 | [diff] [blame] | 1056 | for f := Uniqs(fcheck); f != nil; f = Uniqs(f) { |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1057 | p := f.Prog |
| 1058 | if (p.Info.Flags&LeftRead != 0) && Thearch.Smallindir(&p.From, &fcheck.Prog.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1059 | fcheck.Data = &killed |
| 1060 | return |
| 1061 | } |
| 1062 | |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1063 | if (p.Info.Flags&(RightRead|RightWrite) != 0) && Thearch.Smallindir(&p.To, &fcheck.Prog.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1064 | fcheck.Data = &killed |
| 1065 | return |
| 1066 | } |
| 1067 | |
| 1068 | // Stop if another nil check happens. |
| 1069 | if p.As == obj.ACHECKNIL { |
| 1070 | return |
| 1071 | } |
| 1072 | |
| 1073 | // Stop if value is lost. |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1074 | if (p.Info.Flags&RightWrite != 0) && Thearch.Sameaddr(&p.To, &fcheck.Prog.From) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1075 | return |
| 1076 | } |
| 1077 | |
| 1078 | // Stop if memory write. |
Russ Cox | fd38dbc | 2015-03-16 16:46:25 -0400 | [diff] [blame] | 1079 | if (p.Info.Flags&RightWrite != 0) && !Thearch.Regtyp(&p.To) { |
Russ Cox | 8c195bd | 2015-02-13 14:40:36 -0500 | [diff] [blame] | 1080 | return |
| 1081 | } |
| 1082 | |
| 1083 | // Stop if we jump backward. |
| 1084 | if last != nil && f.Id <= last.Id { |
| 1085 | return |
| 1086 | } |
| 1087 | last = f |
| 1088 | } |
| 1089 | } |