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