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 (a] 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 (a] 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 artihmetic 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, 1) 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, 1) 402 copysub(&p.To, v1, v2, 1) 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, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { 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, 0) 449 } 450 451 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) 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=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 462 } 463 var t int 464 var p *obj.Prog 465 for ; r != nil; r = r.S1 { 466 p = r.Prog 467 if gc.Debug['P'] != 0 { 468 fmt.Printf("%v", p) 469 } 470 if f == 0 && gc.Uniqp(r) == nil { 471 f = 1 472 if gc.Debug['P'] != 0 { 473 fmt.Printf("; merge; f=%d", f) 474 } 475 } 476 477 t = copyu(p, v2, nil) 478 switch t { 479 case 2: /* rar, can't split */ 480 if gc.Debug['P'] != 0 { 481 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 482 } 483 return false 484 485 case 3: /* set */ 486 if gc.Debug['P'] != 0 { 487 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 488 } 489 return true 490 491 case 1, /* used, substitute */ 492 4: /* use and set */ 493 if f != 0 { 494 if gc.Debug['P'] == 0 { 495 return false 496 } 497 if t == 4 { 498 fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 499 } else { 500 fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 501 } 502 return false 503 } 504 505 if copyu(p, v2, v1) != 0 { 506 if gc.Debug['P'] != 0 { 507 fmt.Printf("; sub fail; return 0\n") 508 } 509 return false 510 } 511 512 if gc.Debug['P'] != 0 { 513 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 514 } 515 if t == 4 { 516 if gc.Debug['P'] != 0 { 517 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 518 } 519 return true 520 } 521 } 522 523 if f == 0 { 524 t = copyu(p, v1, nil) 525 if f == 0 && (t == 2 || t == 3 || t == 4) { 526 f = 1 527 if gc.Debug['P'] != 0 { 528 fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) 529 } 530 } 531 } 532 533 if gc.Debug['P'] != 0 { 534 fmt.Printf("\n") 535 } 536 if r.S2 != nil { 537 if !copy1(v1, v2, r.S2, f) { 538 return false 539 } 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, 1) != 0 { 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, 1) != 0 { 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 return copysub(&p.From, v, s, 1) 629 } 630 if copyau(&p.From, v) { 631 return 4 632 } 633 return 3 634 } 635 } 636 637 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { 638 if s != nil { 639 if copysub(&p.From, v, s, 1) != 0 { 640 return 1 641 } 642 return copysub(&p.To, v, s, 1) 643 } 644 645 if copyau(&p.From, v) { 646 return 1 647 } 648 if copyau(&p.To, v) { 649 return 1 650 } 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.Fatal("use of byte register") 664 } 665 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_BL { 666 gc.Fatal("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 /* 718 * substitute s for v in a 719 * return failure to substitute 720 */ 721 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { 722 if copyas(a, v) { 723 reg := int(s.Reg) 724 if reg >= x86.REG_AX && reg <= x86.REG_DI || reg >= x86.REG_X0 && reg <= x86.REG_X7 { 725 if f != 0 { 726 a.Reg = int16(reg) 727 } 728 } 729 730 return 0 731 } 732 733 if regtyp(v) { 734 reg := int(v.Reg) 735 if (a.Type == obj.TYPE_MEM || a.Type == obj.TYPE_ADDR) && int(a.Reg) == reg { 736 if (s.Reg == x86.REG_BP) && a.Index != obj.TYPE_NONE { 737 return 1 /* can't use BP-base with index */ 738 } 739 if f != 0 { 740 a.Reg = s.Reg 741 } 742 } 743 744 // return 0; 745 if int(a.Index) == reg { 746 if f != 0 { 747 a.Index = s.Reg 748 } 749 return 0 750 } 751 752 return 0 753 } 754 755 return 0 756 } 757 758 func conprop(r0 *gc.Flow) { 759 var p *obj.Prog 760 var t int 761 762 p0 := r0.Prog 763 v0 := &p0.To 764 r := r0 765 766 loop: 767 r = gc.Uniqs(r) 768 if r == nil || r == r0 { 769 return 770 } 771 if gc.Uniqp(r) == nil { 772 return 773 } 774 775 p = r.Prog 776 t = copyu(p, v0, nil) 777 switch t { 778 case 0, // miss 779 1: // use 780 goto loop 781 782 case 2, // rar 783 4: // use and set 784 break 785 786 case 3: // set 787 if p.As == p0.As { 788 if p.From.Type == p0.From.Type { 789 if p.From.Reg == p0.From.Reg { 790 if p.From.Node == p0.From.Node { 791 if p.From.Offset == p0.From.Offset { 792 if p.From.Scale == p0.From.Scale { 793 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { 794 if p.From.Index == p0.From.Index { 795 excise(r) 796 goto loop 797 } 798 } 799 } 800 } 801 } 802 } 803 } 804 } 805 } 806 } 807 808 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 809 return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 810 } 811 812 func stackaddr(a *obj.Addr) bool { 813 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP 814 } 815