github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/mips64/peep.go (about) 1 // Derived from Inferno utils/6c/peep.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c 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 31 package mips64 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/mips" 37 "fmt" 38 ) 39 40 var gactive uint32 41 42 func peep(firstp *obj.Prog) { 43 g := gc.Flowstart(firstp, nil) 44 if g == nil { 45 return 46 } 47 gactive = 0 48 49 var p *obj.Prog 50 var r *gc.Flow 51 var t int 52 loop1: 53 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 54 gc.Dumpit("loop1", g.Start, 0) 55 } 56 57 t = 0 58 for r = g.Start; r != nil; r = r.Link { 59 p = r.Prog 60 61 // TODO(austin) Handle smaller moves. arm and amd64 62 // distinguish between moves that moves that *must* 63 // sign/zero extend and moves that don't care so they 64 // can eliminate moves that don't care without 65 // breaking moves that do care. This might let us 66 // simplify or remove the next peep loop, too. 67 if p.As == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD { 68 if regtyp(&p.To) { 69 // Try to eliminate reg->reg moves 70 if regtyp(&p.From) { 71 if isfreg(&p.From) == isfreg(&p.To) { 72 if copyprop(r) { 73 excise(r) 74 t++ 75 } else if subprop(r) && copyprop(r) { 76 excise(r) 77 t++ 78 } 79 } 80 } 81 82 // Convert uses to $0 to uses of R0 and 83 // propagate R0 84 if regzer(&p.From) { 85 if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) { 86 p.From.Type = obj.TYPE_REG 87 p.From.Reg = mips.REGZERO 88 if copyprop(r) { 89 excise(r) 90 t++ 91 } else if subprop(r) && copyprop(r) { 92 excise(r) 93 t++ 94 } 95 } 96 } 97 } 98 } 99 } 100 101 if t != 0 { 102 goto loop1 103 } 104 105 /* 106 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) 107 */ 108 var p1 *obj.Prog 109 var r1 *gc.Flow 110 for r := g.Start; r != nil; r = r.Link { 111 p = r.Prog 112 switch p.As { 113 default: 114 continue 115 116 case mips.AMOVH, 117 mips.AMOVHU, 118 mips.AMOVB, 119 mips.AMOVBU, 120 mips.AMOVW, 121 mips.AMOVWU: 122 if p.To.Type != obj.TYPE_REG { 123 continue 124 } 125 } 126 127 r1 = r.Link 128 if r1 == nil { 129 continue 130 } 131 p1 = r1.Prog 132 if p1.As != p.As { 133 continue 134 } 135 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { 136 continue 137 } 138 if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { 139 continue 140 } 141 excise(r1) 142 } 143 144 gc.Flowend(g) 145 } 146 147 func excise(r *gc.Flow) { 148 p := r.Prog 149 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 150 fmt.Printf("%v ===delete===\n", p) 151 } 152 obj.Nopout(p) 153 gc.Ostats.Ndelmov++ 154 } 155 156 // regzer returns true if a's value is 0 (a is R0 or $0) 157 func regzer(a *obj.Addr) bool { 158 if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { 159 if a.Sym == nil && a.Reg == 0 { 160 if a.Offset == 0 { 161 return true 162 } 163 } 164 } 165 return a.Type == obj.TYPE_REG && a.Reg == mips.REGZERO 166 } 167 168 func regtyp(a *obj.Addr) bool { 169 // TODO(rsc): Floating point register exclusions? 170 return a.Type == obj.TYPE_REG && mips.REG_R0 <= a.Reg && a.Reg <= mips.REG_F31 && a.Reg != mips.REGZERO 171 } 172 173 func isfreg(a *obj.Addr) bool { 174 return mips.REG_F0 <= a.Reg && a.Reg <= mips.REG_F31 175 } 176 177 /* 178 * the idea is to substitute 179 * one register for another 180 * from one MOV to another 181 * MOV a, R1 182 * ADD b, R1 / no use of R2 183 * MOV R1, R2 184 * would be converted to 185 * MOV a, R2 186 * ADD b, R2 187 * MOV R2, R1 188 * hopefully, then the former or latter MOV 189 * will be eliminated by copy propagation. 190 * 191 * r0 (the argument, not the register) is the MOV at the end of the 192 * above sequences. This returns 1 if it modified any instructions. 193 */ 194 func subprop(r0 *gc.Flow) bool { 195 p := r0.Prog 196 v1 := &p.From 197 if !regtyp(v1) { 198 return false 199 } 200 v2 := &p.To 201 if !regtyp(v2) { 202 return false 203 } 204 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 205 if gc.Uniqs(r) == nil { 206 break 207 } 208 p = r.Prog 209 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 210 continue 211 } 212 if p.Info.Flags&gc.Call != 0 { 213 return false 214 } 215 216 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { 217 if p.To.Type == v1.Type { 218 if p.To.Reg == v1.Reg { 219 copysub(&p.To, v1, v2, true) 220 if gc.Debug['P'] != 0 { 221 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 222 if p.From.Type == v2.Type { 223 fmt.Printf(" excise") 224 } 225 fmt.Printf("\n") 226 } 227 228 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 229 p = r.Prog 230 copysub(&p.From, v1, v2, true) 231 copysub1(p, v1, v2, true) 232 copysub(&p.To, v1, v2, true) 233 if gc.Debug['P'] != 0 { 234 fmt.Printf("%v\n", r.Prog) 235 } 236 } 237 238 v1.Reg, v2.Reg = v2.Reg, v1.Reg 239 if gc.Debug['P'] != 0 { 240 fmt.Printf("%v last\n", r.Prog) 241 } 242 return true 243 } 244 } 245 } 246 247 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { 248 break 249 } 250 if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { 251 break 252 } 253 } 254 255 return false 256 } 257 258 /* 259 * The idea is to remove redundant copies. 260 * v1->v2 F=0 261 * (use v2 s/v2/v1/)* 262 * set v1 F=1 263 * use v2 return fail (v1->v2 move must remain) 264 * ----------------- 265 * v1->v2 F=0 266 * (use v2 s/v2/v1/)* 267 * set v1 F=1 268 * set v2 return success (caller can remove v1->v2 move) 269 */ 270 func copyprop(r0 *gc.Flow) bool { 271 p := r0.Prog 272 v1 := &p.From 273 v2 := &p.To 274 if copyas(v1, v2) { 275 if gc.Debug['P'] != 0 { 276 fmt.Printf("eliminating self-move: %v\n", r0.Prog) 277 } 278 return true 279 } 280 281 gactive++ 282 if gc.Debug['P'] != 0 { 283 fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) 284 } 285 return copy1(v1, v2, r0.S1, false) 286 } 287 288 // copy1 replaces uses of v2 with v1 starting at r and returns true if 289 // all uses were rewritten. 290 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { 291 if uint32(r.Active) == gactive { 292 if gc.Debug['P'] != 0 { 293 fmt.Printf("act set; return 1\n") 294 } 295 return true 296 } 297 298 r.Active = int32(gactive) 299 if gc.Debug['P'] != 0 { 300 fmt.Printf("copy1 replace %v with %v f=%v\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) 301 } 302 for ; r != nil; r = r.S1 { 303 p := r.Prog 304 if gc.Debug['P'] != 0 { 305 fmt.Printf("%v", p) 306 } 307 if !f && gc.Uniqp(r) == nil { 308 // Multiple predecessors; conservatively 309 // assume v1 was set on other path 310 f = true 311 312 if gc.Debug['P'] != 0 { 313 fmt.Printf("; merge; f=%v", f) 314 } 315 } 316 317 switch t := copyu(p, v2, nil); t { 318 case 2: /* rar, can't split */ 319 if gc.Debug['P'] != 0 { 320 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 321 } 322 return false 323 324 case 3: /* set */ 325 if gc.Debug['P'] != 0 { 326 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 327 } 328 return true 329 330 case 1, /* used, substitute */ 331 4: /* use and set */ 332 if f { 333 if gc.Debug['P'] == 0 { 334 return false 335 } 336 if t == 4 { 337 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 338 } else { 339 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 340 } 341 return false 342 } 343 344 if copyu(p, v2, v1) != 0 { 345 if gc.Debug['P'] != 0 { 346 fmt.Printf("; sub fail; return 0\n") 347 } 348 return false 349 } 350 351 if gc.Debug['P'] != 0 { 352 fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) 353 } 354 if t == 4 { 355 if gc.Debug['P'] != 0 { 356 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 357 } 358 return true 359 } 360 } 361 362 if !f { 363 t := copyu(p, v1, nil) 364 if t == 2 || t == 3 || t == 4 { 365 f = true 366 if gc.Debug['P'] != 0 { 367 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) 368 } 369 } 370 } 371 372 if gc.Debug['P'] != 0 { 373 fmt.Printf("\n") 374 } 375 if r.S2 != nil { 376 if !copy1(v1, v2, r.S2, f) { 377 return false 378 } 379 } 380 } 381 382 return true 383 } 384 385 // If s==nil, copyu returns the set/use of v in p; otherwise, it 386 // modifies p to replace reads of v with reads of s and returns 0 for 387 // success or non-zero for failure. 388 // 389 // If s==nil, copy returns one of the following values: 390 // 1 if v only used 391 // 2 if v is set and used in one address (read-alter-rewrite; 392 // can't substitute) 393 // 3 if v is only set 394 // 4 if v is set in one address and used in another (so addresses 395 // can be rewritten independently) 396 // 0 otherwise (not touched) 397 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 398 if p.From3Type() != obj.TYPE_NONE { 399 // never generates a from3 400 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) 401 } 402 403 switch p.As { 404 default: 405 fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As)) 406 return 2 407 408 case obj.ANOP, /* read p->from, write p->to */ 409 mips.AMOVV, 410 mips.AMOVF, 411 mips.AMOVD, 412 mips.AMOVH, 413 mips.AMOVHU, 414 mips.AMOVB, 415 mips.AMOVBU, 416 mips.AMOVW, 417 mips.AMOVWU, 418 mips.AMOVFD, 419 mips.AMOVDF, 420 mips.AMOVDW, 421 mips.AMOVWD, 422 mips.AMOVFW, 423 mips.AMOVWF, 424 mips.AMOVDV, 425 mips.AMOVVD, 426 mips.AMOVFV, 427 mips.AMOVVF, 428 mips.ATRUNCFV, 429 mips.ATRUNCDV, 430 mips.ATRUNCFW, 431 mips.ATRUNCDW: 432 if s != nil { 433 if copysub(&p.From, v, s, true) { 434 return 1 435 } 436 437 // Update only indirect uses of v in p->to 438 if !copyas(&p.To, v) { 439 if copysub(&p.To, v, s, true) { 440 return 1 441 } 442 } 443 return 0 444 } 445 446 if copyas(&p.To, v) { 447 // Fix up implicit from 448 if p.From.Type == obj.TYPE_NONE { 449 p.From = p.To 450 } 451 if copyau(&p.From, v) { 452 return 4 453 } 454 return 3 455 } 456 457 if copyau(&p.From, v) { 458 return 1 459 } 460 if copyau(&p.To, v) { 461 // p->to only indirectly uses v 462 return 1 463 } 464 465 return 0 466 467 case mips.ASGT, /* read p->from, read p->reg, write p->to */ 468 mips.ASGTU, 469 470 mips.AADD, 471 mips.AADDU, 472 mips.ASUB, 473 mips.ASUBU, 474 mips.ASLL, 475 mips.ASRL, 476 mips.ASRA, 477 mips.AOR, 478 mips.ANOR, 479 mips.AAND, 480 mips.AXOR, 481 482 mips.AADDV, 483 mips.AADDVU, 484 mips.ASUBV, 485 mips.ASUBVU, 486 mips.ASLLV, 487 mips.ASRLV, 488 mips.ASRAV, 489 490 mips.AADDF, 491 mips.AADDD, 492 mips.ASUBF, 493 mips.ASUBD, 494 mips.AMULF, 495 mips.AMULD, 496 mips.ADIVF, 497 mips.ADIVD: 498 if s != nil { 499 if copysub(&p.From, v, s, true) { 500 return 1 501 } 502 if copysub1(p, v, s, true) { 503 return 1 504 } 505 506 // Update only indirect uses of v in p->to 507 if !copyas(&p.To, v) { 508 if copysub(&p.To, v, s, true) { 509 return 1 510 } 511 } 512 return 0 513 } 514 515 if copyas(&p.To, v) { 516 if p.Reg == 0 { 517 // Fix up implicit reg (e.g., ADD 518 // R3,R4 -> ADD R3,R4,R4) so we can 519 // update reg and to separately. 520 p.Reg = p.To.Reg 521 } 522 523 if copyau(&p.From, v) { 524 return 4 525 } 526 if copyau1(p, v) { 527 return 4 528 } 529 return 3 530 } 531 532 if copyau(&p.From, v) { 533 return 1 534 } 535 if copyau1(p, v) { 536 return 1 537 } 538 if copyau(&p.To, v) { 539 return 1 540 } 541 return 0 542 543 case obj.ACHECKNIL, /* read p->from */ 544 mips.ABEQ, /* read p->from, read p->reg */ 545 mips.ABNE, 546 mips.ABGTZ, 547 mips.ABGEZ, 548 mips.ABLTZ, 549 mips.ABLEZ, 550 551 mips.ACMPEQD, 552 mips.ACMPEQF, 553 mips.ACMPGED, 554 mips.ACMPGEF, 555 mips.ACMPGTD, 556 mips.ACMPGTF, 557 mips.ABFPF, 558 mips.ABFPT, 559 560 mips.AMUL, 561 mips.AMULU, 562 mips.ADIV, 563 mips.ADIVU, 564 mips.AMULV, 565 mips.AMULVU, 566 mips.ADIVV, 567 mips.ADIVVU: 568 if s != nil { 569 if copysub(&p.From, v, s, true) { 570 return 1 571 } 572 if copysub1(p, v, s, true) { 573 return 1 574 } 575 return 0 576 } 577 578 if copyau(&p.From, v) { 579 return 1 580 } 581 if copyau1(p, v) { 582 return 1 583 } 584 return 0 585 586 case mips.AJMP: /* read p->to */ 587 if s != nil { 588 if copysub(&p.To, v, s, true) { 589 return 1 590 } 591 return 0 592 } 593 594 if copyau(&p.To, v) { 595 return 1 596 } 597 return 0 598 599 case mips.ARET: /* funny */ 600 if s != nil { 601 return 0 602 } 603 604 // All registers die at this point, so claim 605 // everything is set (and not used). 606 return 3 607 608 case mips.AJAL: /* funny */ 609 if v.Type == obj.TYPE_REG { 610 // TODO(rsc): REG_R0 and REG_F0 used to be 611 // (when register numbers started at 0) exregoffset and exfregoffset, 612 // which are unset entirely. 613 // It's strange that this handles R0 and F0 differently from the other 614 // registers. Possible failure to optimize? 615 if mips.REG_R0 < v.Reg && v.Reg <= mips.REG_R31 { 616 return 2 617 } 618 if v.Reg == mips.REGARG { 619 return 2 620 } 621 if mips.REG_F0 < v.Reg && v.Reg <= mips.REG_F31 { 622 return 2 623 } 624 } 625 626 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { 627 return 2 628 } 629 630 if s != nil { 631 if copysub(&p.To, v, s, true) { 632 return 1 633 } 634 return 0 635 } 636 637 if copyau(&p.To, v) { 638 return 4 639 } 640 return 3 641 642 // R0 is zero, used by DUFFZERO, cannot be substituted. 643 // R1 is ptr to memory, used and set, cannot be substituted. 644 case obj.ADUFFZERO: 645 if v.Type == obj.TYPE_REG { 646 if v.Reg == 0 { 647 return 1 648 } 649 if v.Reg == 1 { 650 return 2 651 } 652 } 653 654 return 0 655 656 // R1, R2 are ptr to src, dst, used and set, cannot be substituted. 657 // R3 is scratch, set by DUFFCOPY, cannot be substituted. 658 case obj.ADUFFCOPY: 659 if v.Type == obj.TYPE_REG { 660 if v.Reg == 1 || v.Reg == 2 { 661 return 2 662 } 663 if v.Reg == 3 { 664 return 3 665 } 666 } 667 668 return 0 669 670 case obj.ATEXT: /* funny */ 671 if v.Type == obj.TYPE_REG { 672 if v.Reg == mips.REGARG { 673 return 3 674 } 675 } 676 return 0 677 678 case obj.APCDATA, 679 obj.AFUNCDATA, 680 obj.AVARDEF, 681 obj.AVARKILL, 682 obj.AVARLIVE, 683 obj.AUSEFIELD: 684 return 0 685 } 686 } 687 688 // copyas returns 1 if a and v address the same register. 689 // 690 // If a is the from operand, this means this operation reads the 691 // register in v. If a is the to operand, this means this operation 692 // writes the register in v. 693 func copyas(a *obj.Addr, v *obj.Addr) bool { 694 if regtyp(v) { 695 if a.Type == v.Type { 696 if a.Reg == v.Reg { 697 return true 698 } 699 } 700 } 701 return false 702 } 703 704 // copyau returns 1 if a either directly or indirectly addresses the 705 // same register as v. 706 // 707 // If a is the from operand, this means this operation reads the 708 // register in v. If a is the to operand, this means the operation 709 // either reads or writes the register in v (if !copyas(a, v), then 710 // the operation reads the register in v). 711 func copyau(a *obj.Addr, v *obj.Addr) bool { 712 if copyas(a, v) { 713 return true 714 } 715 if v.Type == obj.TYPE_REG { 716 if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { 717 if v.Reg == a.Reg { 718 return true 719 } 720 } 721 } 722 return false 723 } 724 725 // copyau1 returns true if p->reg references the same register as v and v 726 // is a direct reference. 727 func copyau1(p *obj.Prog, v *obj.Addr) bool { 728 return regtyp(v) && v.Reg != 0 && p.Reg == v.Reg 729 } 730 731 // copysub replaces v with s in a if f==true or indicates it if could if f==false. 732 // Returns true on failure to substitute (it always succeeds on mips). 733 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. 734 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { 735 if f && copyau(a, v) { 736 a.Reg = s.Reg 737 } 738 return false 739 } 740 741 // copysub1 replaces v with s in p1->reg if f==true or indicates if it could if f==false. 742 // Returns true on failure to substitute (it always succeeds on mips). 743 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. 744 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { 745 if f && copyau1(p1, v) { 746 p1.Reg = s.Reg 747 } 748 return false 749 } 750 751 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 752 if a.Type != v.Type { 753 return false 754 } 755 if regtyp(v) && a.Reg == v.Reg { 756 return true 757 } 758 if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { 759 if v.Offset == a.Offset { 760 return true 761 } 762 } 763 return false 764 } 765 766 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 767 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 768 } 769 770 func stackaddr(a *obj.Addr) bool { 771 return a.Type == obj.TYPE_REG && a.Reg == mips.REGSP 772 }