github.com/miolini/go@v0.0.0-20160405192216-fca68c8cb408/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 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) 253 } 254 255 // movb elimination. 256 // movb is simulated by the linker 257 // when a register other than ax, bx, cx, dx 258 // is used, so rewrite to other instructions 259 // when possible. a movb into a register 260 // can smash the entire 64-bit register without 261 // causing any trouble. 262 func elimshortmov(g *gc.Graph) { 263 var p *obj.Prog 264 265 for r := g.Start; r != nil; r = r.Link { 266 p = r.Prog 267 if regtyp(&p.To) { 268 switch p.As { 269 case x86.AINCB, 270 x86.AINCW: 271 p.As = x86.AINCL 272 273 case x86.ADECB, 274 x86.ADECW: 275 p.As = x86.ADECL 276 277 case x86.ANEGB, 278 x86.ANEGW: 279 p.As = x86.ANEGL 280 281 case x86.ANOTB, 282 x86.ANOTW: 283 p.As = x86.ANOTL 284 } 285 286 if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { 287 // move or arithmetic into partial register. 288 // from another register or constant can be movl. 289 // we don't switch to 32-bit arithmetic if it can 290 // change how the carry bit is set (and the carry bit is needed). 291 switch p.As { 292 case x86.AMOVB, 293 x86.AMOVW: 294 p.As = x86.AMOVL 295 296 case x86.AADDB, 297 x86.AADDW: 298 if !needc(p.Link) { 299 p.As = x86.AADDL 300 } 301 302 case x86.ASUBB, 303 x86.ASUBW: 304 if !needc(p.Link) { 305 p.As = x86.ASUBL 306 } 307 308 case x86.AMULB, 309 x86.AMULW: 310 p.As = x86.AMULL 311 312 case x86.AIMULB, 313 x86.AIMULW: 314 p.As = x86.AIMULL 315 316 case x86.AANDB, 317 x86.AANDW: 318 p.As = x86.AANDL 319 320 case x86.AORB, 321 x86.AORW: 322 p.As = x86.AORL 323 324 case x86.AXORB, 325 x86.AXORW: 326 p.As = x86.AXORL 327 328 case x86.ASHLB, 329 x86.ASHLW: 330 p.As = x86.ASHLL 331 } 332 } else { 333 // explicit zero extension 334 switch p.As { 335 case x86.AMOVB: 336 p.As = x86.AMOVBLZX 337 338 case x86.AMOVW: 339 p.As = x86.AMOVWLZX 340 } 341 } 342 } 343 } 344 } 345 346 /* 347 * the idea is to substitute 348 * one register for another 349 * from one MOV to another 350 * MOV a, R0 351 * ADD b, R0 / no use of R1 352 * MOV R0, R1 353 * would be converted to 354 * MOV a, R1 355 * ADD b, R1 356 * MOV R1, R0 357 * hopefully, then the former or latter MOV 358 * will be eliminated by copy propagation. 359 */ 360 func subprop(r0 *gc.Flow) bool { 361 p := r0.Prog 362 v1 := &p.From 363 if !regtyp(v1) { 364 return false 365 } 366 v2 := &p.To 367 if !regtyp(v2) { 368 return false 369 } 370 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 371 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 372 fmt.Printf("\t? %v\n", r.Prog) 373 } 374 if gc.Uniqs(r) == nil { 375 break 376 } 377 p = r.Prog 378 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 379 continue 380 } 381 if p.Info.Flags&gc.Call != 0 { 382 return false 383 } 384 385 if p.Info.Reguse|p.Info.Regset != 0 { 386 return false 387 } 388 389 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 { 390 copysub(&p.To, v1, v2, true) 391 if gc.Debug['P'] != 0 { 392 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 393 if p.From.Type == v2.Type && p.From.Reg == v2.Reg { 394 fmt.Printf(" excise") 395 } 396 fmt.Printf("\n") 397 } 398 399 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 400 p = r.Prog 401 copysub(&p.From, v1, v2, true) 402 copysub(&p.To, v1, v2, true) 403 if gc.Debug['P'] != 0 { 404 fmt.Printf("%v\n", r.Prog) 405 } 406 } 407 408 t := int(v1.Reg) 409 v1.Reg = v2.Reg 410 v2.Reg = int16(t) 411 if gc.Debug['P'] != 0 { 412 fmt.Printf("%v last\n", r.Prog) 413 } 414 return true 415 } 416 417 if copyau(&p.From, v2) || copyau(&p.To, v2) { 418 break 419 } 420 if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { 421 break 422 } 423 } 424 425 return false 426 } 427 428 /* 429 * The idea is to remove redundant copies. 430 * v1->v2 F=0 431 * (use v2 s/v2/v1/)* 432 * set v1 F=1 433 * use v2 return fail 434 * ----------------- 435 * v1->v2 F=0 436 * (use v2 s/v2/v1/)* 437 * set v1 F=1 438 * set v2 return success 439 */ 440 func copyprop(g *gc.Graph, r0 *gc.Flow) bool { 441 p := r0.Prog 442 v1 := &p.From 443 v2 := &p.To 444 if copyas(v1, v2) { 445 return true 446 } 447 gactive++ 448 return copy1(v1, v2, r0.S1, false) 449 } 450 451 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { 452 if uint32(r.Active) == gactive { 453 if gc.Debug['P'] != 0 { 454 fmt.Printf("act set; return 1\n") 455 } 456 return true 457 } 458 459 r.Active = int32(gactive) 460 if gc.Debug['P'] != 0 { 461 fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 462 } 463 for ; r != nil; r = r.S1 { 464 p := r.Prog 465 if gc.Debug['P'] != 0 { 466 fmt.Printf("%v", p) 467 } 468 if !f && gc.Uniqp(r) == nil { 469 f = true 470 if gc.Debug['P'] != 0 { 471 fmt.Printf("; merge; f=%v", f) 472 } 473 } 474 475 switch t := copyu(p, v2, nil); t { 476 case 2: /* rar, can't split */ 477 if gc.Debug['P'] != 0 { 478 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 479 } 480 return false 481 482 case 3: /* set */ 483 if gc.Debug['P'] != 0 { 484 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 485 } 486 return true 487 488 case 1, /* used, substitute */ 489 4: /* use and set */ 490 if f { 491 if gc.Debug['P'] == 0 { 492 return false 493 } 494 if t == 4 { 495 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 496 } else { 497 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 498 } 499 return false 500 } 501 502 if copyu(p, v2, v1) != 0 { 503 if gc.Debug['P'] != 0 { 504 fmt.Printf("; sub fail; return 0\n") 505 } 506 return false 507 } 508 509 if gc.Debug['P'] != 0 { 510 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 511 } 512 if t == 4 { 513 if gc.Debug['P'] != 0 { 514 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 515 } 516 return true 517 } 518 } 519 520 if !f { 521 t := copyu(p, v1, nil) 522 if t == 2 || t == 3 || t == 4 { 523 f = true 524 if gc.Debug['P'] != 0 { 525 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) 526 } 527 } 528 } 529 530 if gc.Debug['P'] != 0 { 531 fmt.Printf("\n") 532 } 533 if r.S2 != nil { 534 if !copy1(v1, v2, r.S2, f) { 535 return false 536 } 537 } 538 } 539 return true 540 } 541 542 /* 543 * return 544 * 1 if v only used (and substitute), 545 * 2 if read-alter-rewrite 546 * 3 if set 547 * 4 if set and used 548 * 0 otherwise (not touched) 549 */ 550 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 551 switch p.As { 552 case obj.AJMP: 553 if s != nil { 554 if copysub(&p.To, v, s, true) { 555 return 1 556 } 557 return 0 558 } 559 560 if copyau(&p.To, v) { 561 return 1 562 } 563 return 0 564 565 case obj.ARET: 566 if s != nil { 567 return 1 568 } 569 return 3 570 571 case obj.ACALL: 572 if REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= REGEXT && v.Reg > exregoffset { 573 return 2 574 } 575 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 576 return 2 577 } 578 if v.Type == p.From.Type && v.Reg == p.From.Reg { 579 return 2 580 } 581 582 if s != nil { 583 if copysub(&p.To, v, s, true) { 584 return 1 585 } 586 return 0 587 } 588 589 if copyau(&p.To, v) { 590 return 4 591 } 592 return 3 593 594 case obj.ATEXT: 595 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 596 return 3 597 } 598 return 0 599 } 600 601 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 602 return 0 603 } 604 605 if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { 606 return 2 607 } 608 609 if p.Info.Flags&gc.LeftAddr != 0 { 610 if copyas(&p.From, v) { 611 return 2 612 } 613 } 614 615 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { 616 if copyas(&p.To, v) { 617 return 2 618 } 619 } 620 621 if p.Info.Flags&gc.RightWrite != 0 { 622 if copyas(&p.To, v) { 623 if s != nil { 624 if copysub(&p.From, v, s, true) { 625 return 1 626 } 627 return 0 628 } 629 if copyau(&p.From, v) { 630 return 4 631 } 632 return 3 633 } 634 } 635 636 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { 637 if s != nil { 638 if copysub(&p.From, v, s, true) { 639 return 1 640 } 641 if copysub(&p.To, v, s, true) { 642 return 1 643 } 644 return 0 645 } 646 if copyau(&p.From, v) { 647 return 1 648 } 649 if copyau(&p.To, v) { 650 return 1 651 } 652 } 653 return 0 654 } 655 656 /* 657 * direct reference, 658 * could be set/use depending on 659 * semantics 660 */ 661 func copyas(a *obj.Addr, v *obj.Addr) bool { 662 if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_BL { 663 gc.Fatalf("use of byte register") 664 } 665 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { 666 gc.Fatalf("use of byte register") 667 } 668 669 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 670 return false 671 } 672 if regtyp(v) { 673 return true 674 } 675 if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 676 if v.Offset == a.Offset { 677 return true 678 } 679 } 680 return false 681 } 682 683 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 684 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 685 return false 686 } 687 if regtyp(v) { 688 return true 689 } 690 if (v.Type == obj.TYPE_MEM || v.Type == obj.TYPE_ADDR) && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 691 if v.Offset == a.Offset { 692 return true 693 } 694 } 695 return false 696 } 697 698 /* 699 * either direct or indirect 700 */ 701 func copyau(a *obj.Addr, v *obj.Addr) bool { 702 if copyas(a, v) { 703 return true 704 } 705 if regtyp(v) { 706 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { 707 return true 708 } 709 if a.Index == v.Reg { 710 return true 711 } 712 } 713 714 return false 715 } 716 717 // copysub substitute s for v in a. 718 // copysub returns true on failure to substitute. 719 // TODO(dfc) reverse this logic to return false on sunstitution failure. 720 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { 721 if copyas(a, v) { 722 if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_DI || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X7 { 723 if f { 724 a.Reg = s.Reg 725 } 726 } 727 return false 728 } 729 730 if regtyp(v) { 731 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && a.Reg == v.Reg { 732 if (s.Reg == x86.REG_BP) && a.Index != x86.REG_NONE { 733 return true /* can't use BP-base with index */ 734 } 735 if f { 736 a.Reg = s.Reg 737 } 738 } 739 740 if a.Index == v.Reg { 741 if f { 742 a.Index = s.Reg 743 } 744 } 745 } 746 return false 747 } 748 749 func conprop(r0 *gc.Flow) { 750 var p *obj.Prog 751 752 p0 := r0.Prog 753 v0 := &p0.To 754 r := r0 755 756 loop: 757 r = gc.Uniqs(r) 758 if r == nil || r == r0 { 759 return 760 } 761 if gc.Uniqp(r) == nil { 762 return 763 } 764 765 p = r.Prog 766 switch copyu(p, v0, nil) { 767 case 0, // miss 768 1: // use 769 goto loop 770 771 case 2, // rar 772 4: // use and set 773 break 774 775 case 3: // set 776 if p.As == p0.As { 777 if p.From.Type == p0.From.Type { 778 if p.From.Reg == p0.From.Reg { 779 if p.From.Node == p0.From.Node { 780 if p.From.Offset == p0.From.Offset { 781 if p.From.Scale == p0.From.Scale { 782 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { 783 if p.From.Index == p0.From.Index { 784 excise(r) 785 goto loop 786 } 787 } 788 } 789 } 790 } 791 } 792 } 793 } 794 } 795 } 796 797 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 798 return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 799 } 800 801 func stackaddr(a *obj.Addr) bool { 802 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP 803 }