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