github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/src/cmd/compile/internal/x86/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 x86 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/x86" 37 "fmt" 38 ) 39 40 const ( 41 REGEXT = 0 42 exregoffset = x86.REG_DI 43 ) 44 45 var gactive uint32 46 47 // do we need the carry bit 48 func needc(p *obj.Prog) bool { 49 for p != nil { 50 if p.Info.Flags&gc.UseCarry != 0 { 51 return true 52 } 53 if p.Info.Flags&(gc.SetCarry|gc.KillCarry) != 0 { 54 return false 55 } 56 p = p.Link 57 } 58 59 return false 60 } 61 62 func rnops(r *gc.Flow) *gc.Flow { 63 if r != nil { 64 var p *obj.Prog 65 var r1 *gc.Flow 66 for { 67 p = r.Prog 68 if p.As != obj.ANOP || p.From.Type != obj.TYPE_NONE || p.To.Type != obj.TYPE_NONE { 69 break 70 } 71 r1 = gc.Uniqs(r) 72 if r1 == nil { 73 break 74 } 75 r = r1 76 } 77 } 78 79 return r 80 } 81 82 func peep(firstp *obj.Prog) { 83 g := gc.Flowstart(firstp, nil) 84 if g == nil { 85 return 86 } 87 gactive = 0 88 89 // byte, word arithmetic elimination. 90 elimshortmov(g) 91 92 // constant propagation 93 // find MOV $con,R followed by 94 // another MOV $con,R without 95 // setting R in the interim 96 var p *obj.Prog 97 for r := g.Start; r != nil; r = r.Link { 98 p = r.Prog 99 switch p.As { 100 case x86.ALEAL: 101 if regtyp(&p.To) { 102 if p.From.Sym != nil { 103 if p.From.Index == x86.REG_NONE { 104 conprop(r) 105 } 106 } 107 } 108 109 case x86.AMOVB, 110 x86.AMOVW, 111 x86.AMOVL, 112 x86.AMOVSS, 113 x86.AMOVSD: 114 if regtyp(&p.To) { 115 if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { 116 conprop(r) 117 } 118 } 119 } 120 } 121 122 var r1 *gc.Flow 123 var p1 *obj.Prog 124 var r *gc.Flow 125 var t int 126 loop1: 127 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 128 gc.Dumpit("loop1", g.Start, 0) 129 } 130 131 t = 0 132 for r = g.Start; r != nil; r = r.Link { 133 p = r.Prog 134 switch p.As { 135 case x86.AMOVL, 136 x86.AMOVSS, 137 x86.AMOVSD: 138 if regtyp(&p.To) { 139 if regtyp(&p.From) { 140 if copyprop(g, r) { 141 excise(r) 142 t++ 143 } else if subprop(r) && copyprop(g, r) { 144 excise(r) 145 t++ 146 } 147 } 148 } 149 150 case x86.AMOVBLZX, 151 x86.AMOVWLZX, 152 x86.AMOVBLSX, 153 x86.AMOVWLSX: 154 if regtyp(&p.To) { 155 r1 = rnops(gc.Uniqs(r)) 156 if r1 != nil { 157 p1 = r1.Prog 158 if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { 159 p1.As = x86.AMOVL 160 t++ 161 } 162 } 163 } 164 165 case x86.AADDL, 166 x86.AADDW: 167 if p.From.Type != obj.TYPE_CONST || needc(p.Link) { 168 break 169 } 170 if p.From.Offset == -1 { 171 if p.As == x86.AADDL { 172 p.As = x86.ADECL 173 } else { 174 p.As = x86.ADECW 175 } 176 p.From = obj.Addr{} 177 break 178 } 179 180 if p.From.Offset == 1 { 181 if p.As == x86.AADDL { 182 p.As = x86.AINCL 183 } else { 184 p.As = x86.AINCW 185 } 186 p.From = obj.Addr{} 187 break 188 } 189 190 case x86.ASUBL, 191 x86.ASUBW: 192 if p.From.Type != obj.TYPE_CONST || needc(p.Link) { 193 break 194 } 195 if p.From.Offset == -1 { 196 if p.As == x86.ASUBL { 197 p.As = x86.AINCL 198 } else { 199 p.As = x86.AINCW 200 } 201 p.From = obj.Addr{} 202 break 203 } 204 205 if p.From.Offset == 1 { 206 if p.As == x86.ASUBL { 207 p.As = x86.ADECL 208 } else { 209 p.As = x86.ADECW 210 } 211 p.From = obj.Addr{} 212 break 213 } 214 } 215 } 216 217 if t != 0 { 218 goto loop1 219 } 220 221 // MOVSD removal. 222 // We never use packed registers, so a MOVSD between registers 223 // can be replaced by MOVAPD, which moves the pair of float64s 224 // instead of just the lower one. We only use the lower one, but 225 // the processor can do better if we do moves using both. 226 for r := g.Start; r != nil; r = r.Link { 227 p = r.Prog 228 if p.As == x86.AMOVSD { 229 if regtyp(&p.From) { 230 if regtyp(&p.To) { 231 p.As = x86.AMOVAPD 232 } 233 } 234 } 235 } 236 237 gc.Flowend(g) 238 } 239 240 func excise(r *gc.Flow) { 241 p := r.Prog 242 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 243 fmt.Printf("%v ===delete===\n", p) 244 } 245 246 obj.Nopout(p) 247 248 gc.Ostats.Ndelmov++ 249 } 250 251 func regtyp(a *obj.Addr) bool { 252 if gc.Ctxt.Flag_shared && a.Type == obj.TYPE_REG && a.Reg == x86.REG_CX { 253 // don't propagate CX, it is used implicitly by PIC global references 254 return false 255 } 256 return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_DI || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X7) 257 } 258 259 // movb elimination. 260 // movb is simulated by the linker 261 // when a register other than ax, bx, cx, dx 262 // is used, so rewrite to other instructions 263 // when possible. a movb into a register 264 // can smash the entire 64-bit register without 265 // causing any trouble. 266 func elimshortmov(g *gc.Graph) { 267 var p *obj.Prog 268 269 for r := g.Start; r != nil; r = r.Link { 270 p = r.Prog 271 if regtyp(&p.To) { 272 switch p.As { 273 case x86.AINCB, 274 x86.AINCW: 275 p.As = x86.AINCL 276 277 case x86.ADECB, 278 x86.ADECW: 279 p.As = x86.ADECL 280 281 case x86.ANEGB, 282 x86.ANEGW: 283 p.As = x86.ANEGL 284 285 case x86.ANOTB, 286 x86.ANOTW: 287 p.As = x86.ANOTL 288 } 289 290 if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { 291 // move or arithmetic into partial register. 292 // from another register or constant can be movl. 293 // we don't switch to 32-bit arithmetic if it can 294 // change how the carry bit is set (and the carry bit is needed). 295 switch p.As { 296 case x86.AMOVB, 297 x86.AMOVW: 298 p.As = x86.AMOVL 299 300 case x86.AADDB, 301 x86.AADDW: 302 if !needc(p.Link) { 303 p.As = x86.AADDL 304 } 305 306 case x86.ASUBB, 307 x86.ASUBW: 308 if !needc(p.Link) { 309 p.As = x86.ASUBL 310 } 311 312 case x86.AMULB, 313 x86.AMULW: 314 p.As = x86.AMULL 315 316 case x86.AIMULB, 317 x86.AIMULW: 318 p.As = x86.AIMULL 319 320 case x86.AANDB, 321 x86.AANDW: 322 p.As = x86.AANDL 323 324 case x86.AORB, 325 x86.AORW: 326 p.As = x86.AORL 327 328 case x86.AXORB, 329 x86.AXORW: 330 p.As = x86.AXORL 331 332 case x86.ASHLB, 333 x86.ASHLW: 334 p.As = x86.ASHLL 335 } 336 } else { 337 // explicit zero extension 338 switch p.As { 339 case x86.AMOVB: 340 p.As = x86.AMOVBLZX 341 342 case x86.AMOVW: 343 p.As = x86.AMOVWLZX 344 } 345 } 346 } 347 } 348 } 349 350 /* 351 * the idea is to substitute 352 * one register for another 353 * from one MOV to another 354 * MOV a, R0 355 * ADD b, R0 / no use of R1 356 * MOV R0, R1 357 * would be converted to 358 * MOV a, R1 359 * ADD b, R1 360 * MOV R1, R0 361 * hopefully, then the former or latter MOV 362 * will be eliminated by copy propagation. 363 */ 364 func subprop(r0 *gc.Flow) bool { 365 p := r0.Prog 366 v1 := &p.From 367 if !regtyp(v1) { 368 return false 369 } 370 v2 := &p.To 371 if !regtyp(v2) { 372 return false 373 } 374 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 375 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 376 fmt.Printf("\t? %v\n", r.Prog) 377 } 378 if gc.Uniqs(r) == nil { 379 break 380 } 381 p = r.Prog 382 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 383 continue 384 } 385 if p.Info.Flags&gc.Call != 0 { 386 return false 387 } 388 389 if p.Info.Reguse|p.Info.Regset != 0 { 390 return false 391 } 392 393 if (p.Info.Flags&gc.Move != 0) && (p.Info.Flags&(gc.SizeL|gc.SizeQ|gc.SizeF|gc.SizeD) != 0) && p.To.Type == v1.Type && p.To.Reg == v1.Reg { 394 copysub(&p.To, v1, v2, true) 395 if gc.Debug['P'] != 0 { 396 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 397 if p.From.Type == v2.Type && p.From.Reg == v2.Reg { 398 fmt.Printf(" excise") 399 } 400 fmt.Printf("\n") 401 } 402 403 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 404 p = r.Prog 405 copysub(&p.From, v1, v2, true) 406 copysub(&p.To, v1, v2, true) 407 if gc.Debug['P'] != 0 { 408 fmt.Printf("%v\n", r.Prog) 409 } 410 } 411 412 t := int(v1.Reg) 413 v1.Reg = v2.Reg 414 v2.Reg = int16(t) 415 if gc.Debug['P'] != 0 { 416 fmt.Printf("%v last\n", r.Prog) 417 } 418 return true 419 } 420 421 if copyau(&p.From, v2) || copyau(&p.To, v2) { 422 break 423 } 424 if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { 425 break 426 } 427 } 428 429 return false 430 } 431 432 /* 433 * The idea is to remove redundant copies. 434 * v1->v2 F=0 435 * (use v2 s/v2/v1/)* 436 * set v1 F=1 437 * use v2 return fail 438 * ----------------- 439 * v1->v2 F=0 440 * (use v2 s/v2/v1/)* 441 * set v1 F=1 442 * set v2 return success 443 */ 444 func copyprop(g *gc.Graph, r0 *gc.Flow) bool { 445 p := r0.Prog 446 v1 := &p.From 447 v2 := &p.To 448 if copyas(v1, v2) { 449 return true 450 } 451 gactive++ 452 return copy1(v1, v2, r0.S1, false) 453 } 454 455 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { 456 if uint32(r.Active) == gactive { 457 if gc.Debug['P'] != 0 { 458 fmt.Printf("act set; return 1\n") 459 } 460 return true 461 } 462 463 r.Active = int32(gactive) 464 if gc.Debug['P'] != 0 { 465 fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 466 } 467 for ; r != nil; r = r.S1 { 468 p := r.Prog 469 if gc.Debug['P'] != 0 { 470 fmt.Printf("%v", p) 471 } 472 if !f && gc.Uniqp(r) == nil { 473 f = true 474 if gc.Debug['P'] != 0 { 475 fmt.Printf("; merge; f=%v", f) 476 } 477 } 478 479 switch t := copyu(p, v2, nil); t { 480 case 2: /* rar, can't split */ 481 if gc.Debug['P'] != 0 { 482 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 483 } 484 return false 485 486 case 3: /* set */ 487 if gc.Debug['P'] != 0 { 488 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 489 } 490 return true 491 492 case 1, /* used, substitute */ 493 4: /* use and set */ 494 if f { 495 if gc.Debug['P'] == 0 { 496 return false 497 } 498 if t == 4 { 499 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 500 } else { 501 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 502 } 503 return false 504 } 505 506 if copyu(p, v2, v1) != 0 { 507 if gc.Debug['P'] != 0 { 508 fmt.Printf("; sub fail; return 0\n") 509 } 510 return false 511 } 512 513 if gc.Debug['P'] != 0 { 514 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 515 } 516 if t == 4 { 517 if gc.Debug['P'] != 0 { 518 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 519 } 520 return true 521 } 522 } 523 524 if !f { 525 t := copyu(p, v1, nil) 526 if t == 2 || t == 3 || t == 4 { 527 f = true 528 if gc.Debug['P'] != 0 { 529 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) 530 } 531 } 532 } 533 534 if gc.Debug['P'] != 0 { 535 fmt.Printf("\n") 536 } 537 if r.S2 != nil { 538 if !copy1(v1, v2, r.S2, f) { 539 return false 540 } 541 } 542 } 543 return true 544 } 545 546 /* 547 * return 548 * 1 if v only used (and substitute), 549 * 2 if read-alter-rewrite 550 * 3 if set 551 * 4 if set and used 552 * 0 otherwise (not touched) 553 */ 554 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 555 switch p.As { 556 case obj.AJMP: 557 if s != nil { 558 if copysub(&p.To, v, s, true) { 559 return 1 560 } 561 return 0 562 } 563 564 if copyau(&p.To, v) { 565 return 1 566 } 567 return 0 568 569 case obj.ARET: 570 if s != nil { 571 return 1 572 } 573 return 3 574 575 case obj.ACALL: 576 if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset { 577 return 2 578 } 579 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 580 return 2 581 } 582 if v.Type == p.From.Type && v.Reg == p.From.Reg { 583 return 2 584 } 585 586 if s != nil { 587 if copysub(&p.To, v, s, true) { 588 return 1 589 } 590 return 0 591 } 592 593 if copyau(&p.To, v) { 594 return 4 595 } 596 return 3 597 598 case obj.ATEXT: 599 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 600 return 3 601 } 602 return 0 603 } 604 605 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 606 return 0 607 } 608 609 if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { 610 return 2 611 } 612 613 if p.Info.Flags&gc.LeftAddr != 0 { 614 if copyas(&p.From, v) { 615 return 2 616 } 617 } 618 619 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { 620 if copyas(&p.To, v) { 621 return 2 622 } 623 } 624 625 if p.Info.Flags&gc.RightWrite != 0 { 626 if copyas(&p.To, v) { 627 if s != nil { 628 if copysub(&p.From, v, s, true) { 629 return 1 630 } 631 return 0 632 } 633 if copyau(&p.From, v) { 634 return 4 635 } 636 return 3 637 } 638 } 639 640 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { 641 if s != nil { 642 if copysub(&p.From, v, s, true) { 643 return 1 644 } 645 if copysub(&p.To, v, s, true) { 646 return 1 647 } 648 return 0 649 } 650 if copyau(&p.From, v) { 651 return 1 652 } 653 if copyau(&p.To, v) { 654 return 1 655 } 656 } 657 return 0 658 } 659 660 /* 661 * direct reference, 662 * could be set/use depending on 663 * semantics 664 */ 665 func copyas(a *obj.Addr, v *obj.Addr) bool { 666 if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { 667 gc.Fatalf("use of byte register") 668 } 669 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { 670 gc.Fatalf("use of byte register") 671 } 672 673 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 674 return false 675 } 676 if regtyp(v) { 677 return true 678 } 679 if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 680 if v.Offset == a.Offset { 681 return true 682 } 683 } 684 return false 685 } 686 687 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 688 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 689 return false 690 } 691 if regtyp(v) { 692 return true 693 } 694 if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 695 if v.Offset == a.Offset { 696 return true 697 } 698 } 699 return false 700 } 701 702 /* 703 * either direct or indirect 704 */ 705 func copyau(a *obj.Addr, v *obj.Addr) bool { 706 if copyas(a, v) { 707 return true 708 } 709 if regtyp(v) { 710 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { 711 return true 712 } 713 if a.Index == v.Reg { 714 return true 715 } 716 } 717 718 return false 719 } 720 721 // copysub substitute s for v in a. 722 // copysub returns true on failure to substitute. 723 // TODO(dfc) reverse this logic to return false on sunstitution failure. 724 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { 725 if copyas(a, v) { 726 if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 { 727 if f { 728 a.Reg = s.Reg 729 } 730 } 731 return false 732 } 733 734 if regtyp(v) { 735 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { 736 if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE { 737 return true /* can't use BP-base with index */ 738 } 739 if f { 740 a.Reg = s.Reg 741 } 742 } 743 744 if a.Index == v.Reg { 745 if f { 746 a.Index = s.Reg 747 } 748 } 749 } 750 return false 751 } 752 753 func conprop(r0 *gc.Flow) { 754 var p *obj.Prog 755 756 p0 := r0.Prog 757 v0 := &p0.To 758 r := r0 759 760 loop: 761 r = gc.Uniqs(r) 762 if r == nil || r == r0 { 763 return 764 } 765 if gc.Uniqp(r) == nil { 766 return 767 } 768 769 p = r.Prog 770 switch copyu(p, v0, nil) { 771 case 0, // miss 772 1: // use 773 goto loop 774 775 case 2, // rar 776 4: // use and set 777 break 778 779 case 3: // set 780 if p.As == p0.As { 781 if p.From.Type == p0.From.Type { 782 if p.From.Reg == p0.From.Reg { 783 if p.From.Node == p0.From.Node { 784 if p.From.Offset == p0.From.Offset { 785 if p.From.Scale == p0.From.Scale { 786 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { 787 if p.From.Index == p0.From.Index { 788 excise(r) 789 goto loop 790 } 791 } 792 } 793 } 794 } 795 } 796 } 797 } 798 } 799 } 800 801 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 802 return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 803 } 804 805 func stackaddr(a *obj.Addr) bool { 806 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP 807 }