github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/cmd/compile/internal/arm/peep.go (about) 1 // Inferno utils/5c/peep.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/5c/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 arm 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/arm" 37 "fmt" 38 ) 39 40 var gactive uint32 41 42 // UNUSED 43 func peep(firstp *obj.Prog) { 44 g := gc.Flowstart(firstp, nil) 45 if g == nil { 46 return 47 } 48 gactive = 0 49 50 var p *obj.Prog 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 switch p.As { 61 /* 62 * elide shift into TYPE_SHIFT operand of subsequent instruction 63 */ 64 // if(shiftprop(r)) { 65 // excise(r); 66 // t++; 67 // break; 68 // } 69 case arm.ASLL, 70 arm.ASRL, 71 arm.ASRA: 72 break 73 74 case arm.AMOVB, 75 arm.AMOVH, 76 arm.AMOVW, 77 arm.AMOVF, 78 arm.AMOVD: 79 if regtyp(&p.From) { 80 if p.From.Type == p.To.Type && isfloatreg(&p.From) == isfloatreg(&p.To) { 81 if p.Scond == arm.C_SCOND_NONE { 82 if copyprop(g, r) { 83 excise(r) 84 t++ 85 break 86 } 87 88 if subprop(r) && copyprop(g, r) { 89 excise(r) 90 t++ 91 break 92 } 93 } 94 } 95 } 96 97 case arm.AMOVHS, 98 arm.AMOVHU, 99 arm.AMOVBS, 100 arm.AMOVBU: 101 if p.From.Type == obj.TYPE_REG { 102 if shortprop(r) { 103 t++ 104 } 105 } 106 } 107 } 108 109 /* 110 if(p->scond == C_SCOND_NONE) 111 if(regtyp(&p->to)) 112 if(isdconst(&p->from)) { 113 constprop(&p->from, &p->to, r->s1); 114 } 115 break; 116 */ 117 if t != 0 { 118 goto loop1 119 } 120 121 for r := g.Start; r != nil; r = r.Link { 122 p = r.Prog 123 switch p.As { 124 /* 125 * EOR -1,x,y => MVN x,y 126 */ 127 case arm.AEOR: 128 if isdconst(&p.From) && p.From.Offset == -1 { 129 p.As = arm.AMVN 130 p.From.Type = obj.TYPE_REG 131 if p.Reg != 0 { 132 p.From.Reg = p.Reg 133 } else { 134 p.From.Reg = p.To.Reg 135 } 136 p.Reg = 0 137 } 138 } 139 } 140 141 for r := g.Start; r != nil; r = r.Link { 142 p = r.Prog 143 switch p.As { 144 case arm.AMOVW, 145 arm.AMOVB, 146 arm.AMOVBS, 147 arm.AMOVBU: 148 if p.From.Type == obj.TYPE_MEM && p.From.Offset == 0 { 149 xtramodes(g, r, &p.From) 150 } else if p.To.Type == obj.TYPE_MEM && p.To.Offset == 0 { 151 xtramodes(g, r, &p.To) 152 } else { 153 continue 154 } 155 } 156 } 157 158 // case ACMP: 159 // /* 160 // * elide CMP $0,x if calculation of x can set condition codes 161 // */ 162 // if(isdconst(&p->from) || p->from.offset != 0) 163 // continue; 164 // r2 = r->s1; 165 // if(r2 == nil) 166 // continue; 167 // t = r2->prog->as; 168 // switch(t) { 169 // default: 170 // continue; 171 // case ABEQ: 172 // case ABNE: 173 // case ABMI: 174 // case ABPL: 175 // break; 176 // case ABGE: 177 // t = ABPL; 178 // break; 179 // case ABLT: 180 // t = ABMI; 181 // break; 182 // case ABHI: 183 // t = ABNE; 184 // break; 185 // case ABLS: 186 // t = ABEQ; 187 // break; 188 // } 189 // r1 = r; 190 // do 191 // r1 = uniqp(r1); 192 // while (r1 != nil && r1->prog->as == ANOP); 193 // if(r1 == nil) 194 // continue; 195 // p1 = r1->prog; 196 // if(p1->to.type != TYPE_REG) 197 // continue; 198 // if(p1->to.reg != p->reg) 199 // if(!(p1->as == AMOVW && p1->from.type == TYPE_REG && p1->from.reg == p->reg)) 200 // continue; 201 // 202 // switch(p1->as) { 203 // default: 204 // continue; 205 // case AMOVW: 206 // if(p1->from.type != TYPE_REG) 207 // continue; 208 // case AAND: 209 // case AEOR: 210 // case AORR: 211 // case ABIC: 212 // case AMVN: 213 // case ASUB: 214 // case ARSB: 215 // case AADD: 216 // case AADC: 217 // case ASBC: 218 // case ARSC: 219 // break; 220 // } 221 // p1->scond |= C_SBIT; 222 // r2->prog->as = t; 223 // excise(r); 224 // continue; 225 226 // predicate(g); 227 228 gc.Flowend(g) 229 } 230 231 func regtyp(a *obj.Addr) bool { 232 return a.Type == obj.TYPE_REG && (arm.REG_R0 <= a.Reg && a.Reg <= arm.REG_R15 || arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15) 233 } 234 235 /* 236 * the idea is to substitute 237 * one register for another 238 * from one MOV to another 239 * MOV a, R0 240 * ADD b, R0 / no use of R1 241 * MOV R0, R1 242 * would be converted to 243 * MOV a, R1 244 * ADD b, R1 245 * MOV R1, R0 246 * hopefully, then the former or latter MOV 247 * will be eliminated by copy propagation. 248 */ 249 func subprop(r0 *gc.Flow) bool { 250 p := r0.Prog 251 v1 := &p.From 252 if !regtyp(v1) { 253 return false 254 } 255 v2 := &p.To 256 if !regtyp(v2) { 257 return false 258 } 259 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 260 if gc.Uniqs(r) == nil { 261 break 262 } 263 p = r.Prog 264 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 265 continue 266 } 267 if p.Info.Flags&gc.Call != 0 { 268 return false 269 } 270 271 // TODO(rsc): Whatever invalidated the info should have done this call. 272 proginfo(p) 273 274 if (p.Info.Flags&gc.CanRegRead != 0) && p.To.Type == obj.TYPE_REG { 275 p.Info.Flags |= gc.RegRead 276 p.Info.Flags &^= (gc.CanRegRead | gc.RightRead) 277 p.Reg = p.To.Reg 278 } 279 280 switch p.As { 281 case arm.AMULLU, 282 arm.AMULA, 283 arm.AMVN: 284 return false 285 } 286 287 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { 288 if p.To.Type == v1.Type { 289 if p.To.Reg == v1.Reg { 290 if p.Scond == arm.C_SCOND_NONE { 291 copysub(&p.To, v1, v2, true) 292 if gc.Debug['P'] != 0 { 293 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 294 if p.From.Type == v2.Type { 295 fmt.Printf(" excise") 296 } 297 fmt.Printf("\n") 298 } 299 300 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 301 p = r.Prog 302 copysub(&p.From, v1, v2, true) 303 copysub1(p, v1, v2, true) 304 copysub(&p.To, v1, v2, true) 305 if gc.Debug['P'] != 0 { 306 fmt.Printf("%v\n", r.Prog) 307 } 308 } 309 310 v1.Reg, v2.Reg = v2.Reg, v1.Reg 311 if gc.Debug['P'] != 0 { 312 fmt.Printf("%v last\n", r.Prog) 313 } 314 return true 315 } 316 } 317 } 318 } 319 320 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { 321 break 322 } 323 if copysub(&p.From, v1, v2, false) || copysub1(p, v1, v2, false) || copysub(&p.To, v1, v2, false) { 324 break 325 } 326 } 327 328 return false 329 } 330 331 /* 332 * The idea is to remove redundant copies. 333 * v1->v2 F=0 334 * (use v2 s/v2/v1/)* 335 * set v1 F=1 336 * use v2 return fail 337 * ----------------- 338 * v1->v2 F=0 339 * (use v2 s/v2/v1/)* 340 * set v1 F=1 341 * set v2 return success 342 */ 343 func copyprop(g *gc.Graph, r0 *gc.Flow) bool { 344 p := r0.Prog 345 v1 := &p.From 346 v2 := &p.To 347 if copyas(v1, v2) { 348 return true 349 } 350 gactive++ 351 return copy1(v1, v2, r0.S1, false) 352 } 353 354 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { 355 if uint32(r.Active) == gactive { 356 if gc.Debug['P'] != 0 { 357 fmt.Printf("act set; return 1\n") 358 } 359 return true 360 } 361 362 r.Active = int32(gactive) 363 if gc.Debug['P'] != 0 { 364 fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 365 } 366 for ; r != nil; r = r.S1 { 367 p := r.Prog 368 if gc.Debug['P'] != 0 { 369 fmt.Printf("%v", p) 370 } 371 if !f && gc.Uniqp(r) == nil { 372 f = true 373 if gc.Debug['P'] != 0 { 374 fmt.Printf("; merge; f=%v", f) 375 } 376 } 377 378 switch t := copyu(p, v2, nil); t { 379 case 2: /* rar, can't split */ 380 if gc.Debug['P'] != 0 { 381 fmt.Printf("; %vrar; return 0\n", gc.Ctxt.Dconv(v2)) 382 } 383 return false 384 385 case 3: /* set */ 386 if gc.Debug['P'] != 0 { 387 fmt.Printf("; %vset; return 1\n", gc.Ctxt.Dconv(v2)) 388 } 389 return true 390 391 case 1, /* used, substitute */ 392 4: /* use and set */ 393 if f { 394 if gc.Debug['P'] == 0 { 395 return false 396 } 397 if t == 4 { 398 fmt.Printf("; %vused+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 399 } else { 400 fmt.Printf("; %vused and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 401 } 402 return false 403 } 404 405 if copyu(p, v2, v1) != 0 { 406 if gc.Debug['P'] != 0 { 407 fmt.Printf("; sub fail; return 0\n") 408 } 409 return false 410 } 411 412 if gc.Debug['P'] != 0 { 413 fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 414 } 415 if t == 4 { 416 if gc.Debug['P'] != 0 { 417 fmt.Printf("; %vused+set; return 1\n", gc.Ctxt.Dconv(v2)) 418 } 419 return true 420 } 421 } 422 423 if !f { 424 t := copyu(p, v1, nil) 425 if t == 2 || t == 3 || t == 4 { 426 f = true 427 if gc.Debug['P'] != 0 { 428 fmt.Printf("; %vset and !f; f=%v", gc.Ctxt.Dconv(v1), f) 429 } 430 } 431 } 432 433 if gc.Debug['P'] != 0 { 434 fmt.Printf("\n") 435 } 436 if r.S2 != nil { 437 if !copy1(v1, v2, r.S2, f) { 438 return false 439 } 440 } 441 } 442 return true 443 } 444 445 // UNUSED 446 /* 447 * The idea is to remove redundant constants. 448 * $c1->v1 449 * ($c1->v2 s/$c1/v1)* 450 * set v1 return 451 * The v1->v2 should be eliminated by copy propagation. 452 */ 453 func constprop(c1 *obj.Addr, v1 *obj.Addr, r *gc.Flow) { 454 if gc.Debug['P'] != 0 { 455 fmt.Printf("constprop %v->%v\n", gc.Ctxt.Dconv(c1), gc.Ctxt.Dconv(v1)) 456 } 457 var p *obj.Prog 458 for ; r != nil; r = r.S1 { 459 p = r.Prog 460 if gc.Debug['P'] != 0 { 461 fmt.Printf("%v", p) 462 } 463 if gc.Uniqp(r) == nil { 464 if gc.Debug['P'] != 0 { 465 fmt.Printf("; merge; return\n") 466 } 467 return 468 } 469 470 if p.As == arm.AMOVW && copyas(&p.From, c1) { 471 if gc.Debug['P'] != 0 { 472 fmt.Printf("; sub%v/%v", gc.Ctxt.Dconv(&p.From), gc.Ctxt.Dconv(v1)) 473 } 474 p.From = *v1 475 } else if copyu(p, v1, nil) > 1 { 476 if gc.Debug['P'] != 0 { 477 fmt.Printf("; %vset; return\n", gc.Ctxt.Dconv(v1)) 478 } 479 return 480 } 481 482 if gc.Debug['P'] != 0 { 483 fmt.Printf("\n") 484 } 485 if r.S2 != nil { 486 constprop(c1, v1, r.S2) 487 } 488 } 489 } 490 491 /* 492 * shortprop eliminates redundant zero/sign extensions. 493 * 494 * MOVBS x, R 495 * <no use R> 496 * MOVBS R, R' 497 * 498 * changed to 499 * 500 * MOVBS x, R 501 * ... 502 * MOVB R, R' (compiled to mov) 503 * 504 * MOVBS above can be a MOVBS, MOVBU, MOVHS or MOVHU. 505 */ 506 func shortprop(r *gc.Flow) bool { 507 p := r.Prog 508 r1 := findpre(r, &p.From) 509 if r1 == nil { 510 return false 511 } 512 513 p1 := r1.Prog 514 if p1.As == p.As { 515 // Two consecutive extensions. 516 goto gotit 517 } 518 519 if p1.As == arm.AMOVW && isdconst(&p1.From) && p1.From.Offset >= 0 && p1.From.Offset < 128 { 520 // Loaded an immediate. 521 goto gotit 522 } 523 524 return false 525 526 gotit: 527 if gc.Debug['P'] != 0 { 528 fmt.Printf("shortprop\n%v\n%v", p1, p) 529 } 530 switch p.As { 531 case arm.AMOVBS, 532 arm.AMOVBU: 533 p.As = arm.AMOVB 534 535 case arm.AMOVHS, 536 arm.AMOVHU: 537 p.As = arm.AMOVH 538 } 539 540 if gc.Debug['P'] != 0 { 541 fmt.Printf(" => %v\n", obj.Aconv(p.As)) 542 } 543 return true 544 } 545 546 // UNUSED 547 /* 548 * ASLL x,y,w 549 * .. (not use w, not set x y w) 550 * AXXX w,a,b (a != w) 551 * .. (not use w) 552 * (set w) 553 * ----------- changed to 554 * .. 555 * AXXX (x<<y),a,b 556 * .. 557 */ 558 func shiftprop(r *gc.Flow) bool { 559 p := r.Prog 560 if p.To.Type != obj.TYPE_REG { 561 if gc.Debug['P'] != 0 { 562 fmt.Printf("\tBOTCH: result not reg; FAILURE\n") 563 } 564 return false 565 } 566 567 n := p.To.Reg 568 var a obj.Addr 569 if p.Reg != 0 && p.Reg != p.To.Reg { 570 a.Type = obj.TYPE_REG 571 a.Reg = p.Reg 572 } 573 574 if gc.Debug['P'] != 0 { 575 fmt.Printf("shiftprop\n%v", p) 576 } 577 r1 := r 578 var p1 *obj.Prog 579 for { 580 /* find first use of shift result; abort if shift operands or result are changed */ 581 r1 = gc.Uniqs(r1) 582 583 if r1 == nil { 584 if gc.Debug['P'] != 0 { 585 fmt.Printf("\tbranch; FAILURE\n") 586 } 587 return false 588 } 589 590 if gc.Uniqp(r1) == nil { 591 if gc.Debug['P'] != 0 { 592 fmt.Printf("\tmerge; FAILURE\n") 593 } 594 return false 595 } 596 597 p1 = r1.Prog 598 if gc.Debug['P'] != 0 { 599 fmt.Printf("\n%v", p1) 600 } 601 switch copyu(p1, &p.To, nil) { 602 case 0: /* not used or set */ 603 if (p.From.Type == obj.TYPE_REG && copyu(p1, &p.From, nil) > 1) || (a.Type == obj.TYPE_REG && copyu(p1, &a, nil) > 1) { 604 if gc.Debug['P'] != 0 { 605 fmt.Printf("\targs modified; FAILURE\n") 606 } 607 return false 608 } 609 610 continue 611 case 3: /* set, not used */ 612 { 613 if gc.Debug['P'] != 0 { 614 fmt.Printf("\tBOTCH: noref; FAILURE\n") 615 } 616 return false 617 } 618 } 619 620 break 621 } 622 623 /* check whether substitution can be done */ 624 switch p1.As { 625 default: 626 if gc.Debug['P'] != 0 { 627 fmt.Printf("\tnon-dpi; FAILURE\n") 628 } 629 return false 630 631 case arm.AAND, 632 arm.AEOR, 633 arm.AADD, 634 arm.AADC, 635 arm.AORR, 636 arm.ASUB, 637 arm.ASBC, 638 arm.ARSB, 639 arm.ARSC: 640 if p1.Reg == n || (p1.Reg == 0 && p1.To.Type == obj.TYPE_REG && p1.To.Reg == n) { 641 if p1.From.Type != obj.TYPE_REG { 642 if gc.Debug['P'] != 0 { 643 fmt.Printf("\tcan't swap; FAILURE\n") 644 } 645 return false 646 } 647 648 p1.Reg = p1.From.Reg 649 p1.From.Reg = n 650 switch p1.As { 651 case arm.ASUB: 652 p1.As = arm.ARSB 653 654 case arm.ARSB: 655 p1.As = arm.ASUB 656 657 case arm.ASBC: 658 p1.As = arm.ARSC 659 660 case arm.ARSC: 661 p1.As = arm.ASBC 662 } 663 664 if gc.Debug['P'] != 0 { 665 fmt.Printf("\t=>%v", p1) 666 } 667 } 668 fallthrough 669 670 case arm.ABIC, 671 arm.ATST, 672 arm.ACMP, 673 arm.ACMN: 674 if p1.Reg == n { 675 if gc.Debug['P'] != 0 { 676 fmt.Printf("\tcan't swap; FAILURE\n") 677 } 678 return false 679 } 680 681 if p1.Reg == 0 && p1.To.Reg == n { 682 if gc.Debug['P'] != 0 { 683 fmt.Printf("\tshift result used twice; FAILURE\n") 684 } 685 return false 686 } 687 688 // case AMVN: 689 if p1.From.Type == obj.TYPE_SHIFT { 690 if gc.Debug['P'] != 0 { 691 fmt.Printf("\tshift result used in shift; FAILURE\n") 692 } 693 return false 694 } 695 696 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != n { 697 if gc.Debug['P'] != 0 { 698 fmt.Printf("\tBOTCH: where is it used?; FAILURE\n") 699 } 700 return false 701 } 702 } 703 704 /* check whether shift result is used subsequently */ 705 p2 := p1 706 707 if p1.To.Reg != n { 708 var p1 *obj.Prog 709 for { 710 r1 = gc.Uniqs(r1) 711 if r1 == nil { 712 if gc.Debug['P'] != 0 { 713 fmt.Printf("\tinconclusive; FAILURE\n") 714 } 715 return false 716 } 717 718 p1 = r1.Prog 719 if gc.Debug['P'] != 0 { 720 fmt.Printf("\n%v", p1) 721 } 722 switch copyu(p1, &p.To, nil) { 723 case 0: /* not used or set */ 724 continue 725 726 case 3: /* set, not used */ 727 break 728 729 default: /* used */ 730 if gc.Debug['P'] != 0 { 731 fmt.Printf("\treused; FAILURE\n") 732 } 733 return false 734 } 735 736 break 737 } 738 } 739 740 /* make the substitution */ 741 p2.From.Reg = 0 742 o := p.Reg 743 if o == 0 { 744 o = p.To.Reg 745 } 746 o &= 15 747 748 switch p.From.Type { 749 case obj.TYPE_CONST: 750 o |= int16(p.From.Offset&0x1f) << 7 751 752 case obj.TYPE_REG: 753 o |= 1<<4 | (p.From.Reg&15)<<8 754 } 755 756 switch p.As { 757 case arm.ASLL: 758 o |= 0 << 5 759 760 case arm.ASRL: 761 o |= 1 << 5 762 763 case arm.ASRA: 764 o |= 2 << 5 765 } 766 767 p2.From = obj.Addr{} 768 p2.From.Type = obj.TYPE_SHIFT 769 p2.From.Offset = int64(o) 770 if gc.Debug['P'] != 0 { 771 fmt.Printf("\t=>%v\tSUCCEED\n", p2) 772 } 773 return true 774 } 775 776 /* 777 * findpre returns the last instruction mentioning v 778 * before r. It must be a set, and there must be 779 * a unique path from that instruction to r. 780 */ 781 func findpre(r *gc.Flow, v *obj.Addr) *gc.Flow { 782 var r1 *gc.Flow 783 784 for r1 = gc.Uniqp(r); r1 != nil; r, r1 = r1, gc.Uniqp(r1) { 785 if gc.Uniqs(r1) != r { 786 return nil 787 } 788 switch copyu(r1.Prog, v, nil) { 789 case 1, /* used */ 790 2: /* read-alter-rewrite */ 791 return nil 792 793 case 3, /* set */ 794 4: /* set and used */ 795 return r1 796 } 797 } 798 799 return nil 800 } 801 802 /* 803 * findinc finds ADD instructions with a constant 804 * argument which falls within the immed_12 range. 805 */ 806 func findinc(r *gc.Flow, r2 *gc.Flow, v *obj.Addr) *gc.Flow { 807 var r1 *gc.Flow 808 var p *obj.Prog 809 810 for r1 = gc.Uniqs(r); r1 != nil && r1 != r2; r, r1 = r1, gc.Uniqs(r1) { 811 if gc.Uniqp(r1) != r { 812 return nil 813 } 814 switch copyu(r1.Prog, v, nil) { 815 case 0: /* not touched */ 816 continue 817 818 case 4: /* set and used */ 819 p = r1.Prog 820 821 if p.As == arm.AADD { 822 if isdconst(&p.From) { 823 if p.From.Offset > -4096 && p.From.Offset < 4096 { 824 return r1 825 } 826 } 827 } 828 fallthrough 829 830 default: 831 return nil 832 } 833 } 834 835 return nil 836 } 837 838 func nochange(r *gc.Flow, r2 *gc.Flow, p *obj.Prog) bool { 839 if r == r2 { 840 return true 841 } 842 n := int(0) 843 var a [3]obj.Addr 844 if p.Reg != 0 && p.Reg != p.To.Reg { 845 a[n].Type = obj.TYPE_REG 846 a[n].Reg = p.Reg 847 n++ 848 } 849 850 switch p.From.Type { 851 case obj.TYPE_SHIFT: 852 a[n].Type = obj.TYPE_REG 853 a[n].Reg = int16(arm.REG_R0 + (p.From.Offset & 0xf)) 854 n++ 855 fallthrough 856 857 case obj.TYPE_REG: 858 a[n].Type = obj.TYPE_REG 859 a[n].Reg = p.From.Reg 860 n++ 861 } 862 863 if n == 0 { 864 return true 865 } 866 var i int 867 for ; r != nil && r != r2; r = gc.Uniqs(r) { 868 p = r.Prog 869 for i = 0; i < n; i++ { 870 if copyu(p, &a[i], nil) > 1 { 871 return false 872 } 873 } 874 } 875 876 return true 877 } 878 879 func findu1(r *gc.Flow, v *obj.Addr) bool { 880 for ; r != nil; r = r.S1 { 881 if r.Active != 0 { 882 return false 883 } 884 r.Active = 1 885 switch copyu(r.Prog, v, nil) { 886 case 1, /* used */ 887 2, /* read-alter-rewrite */ 888 4: /* set and used */ 889 return true 890 891 case 3: /* set */ 892 return false 893 } 894 895 if r.S2 != nil { 896 if findu1(r.S2, v) { 897 return true 898 } 899 } 900 } 901 902 return false 903 } 904 905 func finduse(g *gc.Graph, r *gc.Flow, v *obj.Addr) bool { 906 for r1 := g.Start; r1 != nil; r1 = r1.Link { 907 r1.Active = 0 908 } 909 return findu1(r, v) 910 } 911 912 /* 913 * xtramodes enables the ARM post increment and 914 * shift offset addressing modes to transform 915 * MOVW 0(R3),R1 916 * ADD $4,R3,R3 917 * into 918 * MOVW.P 4(R3),R1 919 * and 920 * ADD R0,R1 921 * MOVBU 0(R1),R0 922 * into 923 * MOVBU R0<<0(R1),R0 924 */ 925 func xtramodes(g *gc.Graph, r *gc.Flow, a *obj.Addr) bool { 926 p := r.Prog 927 v := *a 928 v.Type = obj.TYPE_REG 929 r1 := findpre(r, &v) 930 if r1 != nil { 931 p1 := r1.Prog 932 if p1.To.Type == obj.TYPE_REG && p1.To.Reg == v.Reg { 933 switch p1.As { 934 case arm.AADD: 935 if p1.Scond&arm.C_SBIT != 0 { 936 // avoid altering ADD.S/ADC sequences. 937 break 938 } 939 940 if p1.From.Type == obj.TYPE_REG || (p1.From.Type == obj.TYPE_SHIFT && p1.From.Offset&(1<<4) == 0 && ((p.As != arm.AMOVB && p.As != arm.AMOVBS) || (a == &p.From && p1.From.Offset&^0xf == 0))) || ((p1.From.Type == obj.TYPE_ADDR || p1.From.Type == obj.TYPE_CONST) && p1.From.Offset > -4096 && p1.From.Offset < 4096) { 941 if nochange(gc.Uniqs(r1), r, p1) { 942 if a != &p.From || v.Reg != p.To.Reg { 943 if finduse(g, r.S1, &v) { 944 if p1.Reg == 0 || p1.Reg == v.Reg { 945 /* pre-indexing */ 946 p.Scond |= arm.C_WBIT 947 } else { 948 return false 949 } 950 } 951 } 952 953 switch p1.From.Type { 954 /* register offset */ 955 case obj.TYPE_REG: 956 if gc.Nacl { 957 return false 958 } 959 *a = obj.Addr{} 960 a.Type = obj.TYPE_SHIFT 961 a.Offset = int64(p1.From.Reg) & 15 962 963 /* scaled register offset */ 964 case obj.TYPE_SHIFT: 965 if gc.Nacl { 966 return false 967 } 968 *a = obj.Addr{} 969 a.Type = obj.TYPE_SHIFT 970 fallthrough 971 972 /* immediate offset */ 973 case obj.TYPE_CONST, 974 obj.TYPE_ADDR: 975 a.Offset = p1.From.Offset 976 } 977 978 if p1.Reg != 0 { 979 a.Reg = p1.Reg 980 } 981 excise(r1) 982 return true 983 } 984 } 985 986 case arm.AMOVW: 987 if p1.From.Type == obj.TYPE_REG { 988 r2 := findinc(r1, r, &p1.From) 989 if r2 != nil { 990 var r3 *gc.Flow 991 for r3 = gc.Uniqs(r2); r3.Prog.As == obj.ANOP; r3 = gc.Uniqs(r3) { 992 } 993 if r3 == r { 994 /* post-indexing */ 995 p1 := r2.Prog 996 997 a.Reg = p1.To.Reg 998 a.Offset = p1.From.Offset 999 p.Scond |= arm.C_PBIT 1000 if !finduse(g, r, &r1.Prog.To) { 1001 excise(r1) 1002 } 1003 excise(r2) 1004 return true 1005 } 1006 } 1007 } 1008 } 1009 } 1010 } 1011 1012 if a != &p.From || a.Reg != p.To.Reg { 1013 r1 := findinc(r, nil, &v) 1014 if r1 != nil { 1015 /* post-indexing */ 1016 p1 := r1.Prog 1017 1018 a.Offset = p1.From.Offset 1019 p.Scond |= arm.C_PBIT 1020 excise(r1) 1021 return true 1022 } 1023 } 1024 1025 return false 1026 } 1027 1028 /* 1029 * return 1030 * 1 if v only used (and substitute), 1031 * 2 if read-alter-rewrite 1032 * 3 if set 1033 * 4 if set and used 1034 * 0 otherwise (not touched) 1035 */ 1036 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 1037 switch p.As { 1038 default: 1039 fmt.Printf("copyu: can't find %v\n", obj.Aconv(p.As)) 1040 return 2 1041 1042 case arm.AMOVM: 1043 if v.Type != obj.TYPE_REG { 1044 return 0 1045 } 1046 if p.From.Type == obj.TYPE_CONST { /* read reglist, read/rar */ 1047 if s != nil { 1048 if p.From.Offset&(1<<uint(v.Reg)) != 0 { 1049 return 1 1050 } 1051 if copysub(&p.To, v, s, true) { 1052 return 1 1053 } 1054 return 0 1055 } 1056 1057 if copyau(&p.To, v) { 1058 if p.Scond&arm.C_WBIT != 0 { 1059 return 2 1060 } 1061 return 1 1062 } 1063 1064 if p.From.Offset&(1<<uint(v.Reg)) != 0 { 1065 return 1 /* read/rar, write reglist */ 1066 } 1067 } else { 1068 if s != nil { 1069 if p.To.Offset&(1<<uint(v.Reg)) != 0 { 1070 return 1 1071 } 1072 if copysub(&p.From, v, s, true) { 1073 return 1 1074 } 1075 return 0 1076 } 1077 1078 if copyau(&p.From, v) { 1079 if p.Scond&arm.C_WBIT != 0 { 1080 return 2 1081 } 1082 if p.To.Offset&(1<<uint(v.Reg)) != 0 { 1083 return 4 1084 } 1085 return 1 1086 } 1087 1088 if p.To.Offset&(1<<uint(v.Reg)) != 0 { 1089 return 3 1090 } 1091 } 1092 1093 return 0 1094 1095 case obj.ANOP, /* read,, write */ 1096 arm.ASQRTD, 1097 arm.AMOVW, 1098 arm.AMOVF, 1099 arm.AMOVD, 1100 arm.AMOVH, 1101 arm.AMOVHS, 1102 arm.AMOVHU, 1103 arm.AMOVB, 1104 arm.AMOVBS, 1105 arm.AMOVBU, 1106 arm.AMOVFW, 1107 arm.AMOVWF, 1108 arm.AMOVDW, 1109 arm.AMOVWD, 1110 arm.AMOVFD, 1111 arm.AMOVDF: 1112 if p.Scond&(arm.C_WBIT|arm.C_PBIT) != 0 { 1113 if v.Type == obj.TYPE_REG { 1114 if p.From.Type == obj.TYPE_MEM || p.From.Type == obj.TYPE_SHIFT { 1115 if p.From.Reg == v.Reg { 1116 return 2 1117 } 1118 } else { 1119 if p.To.Reg == v.Reg { 1120 return 2 1121 } 1122 } 1123 } 1124 } 1125 1126 if s != nil { 1127 if copysub(&p.From, v, s, true) { 1128 return 1 1129 } 1130 if !copyas(&p.To, v) { 1131 if copysub(&p.To, v, s, true) { 1132 return 1 1133 } 1134 } 1135 return 0 1136 } 1137 1138 if copyas(&p.To, v) { 1139 if p.Scond != arm.C_SCOND_NONE { 1140 return 2 1141 } 1142 if copyau(&p.From, v) { 1143 return 4 1144 } 1145 return 3 1146 } 1147 1148 if copyau(&p.From, v) { 1149 return 1 1150 } 1151 if copyau(&p.To, v) { 1152 return 1 1153 } 1154 return 0 1155 1156 case arm.AMULLU, /* read, read, write, write */ 1157 arm.AMULL, 1158 arm.AMULA, 1159 arm.AMVN: 1160 return 2 1161 1162 case arm.AADD, /* read, read, write */ 1163 arm.AADC, 1164 arm.ASUB, 1165 arm.ASBC, 1166 arm.ARSB, 1167 arm.ASLL, 1168 arm.ASRL, 1169 arm.ASRA, 1170 arm.AORR, 1171 arm.AAND, 1172 arm.AEOR, 1173 arm.AMUL, 1174 arm.AMULU, 1175 arm.ADIV, 1176 arm.ADIVU, 1177 arm.AMOD, 1178 arm.AMODU, 1179 arm.AADDF, 1180 arm.AADDD, 1181 arm.ASUBF, 1182 arm.ASUBD, 1183 arm.AMULF, 1184 arm.AMULD, 1185 arm.ADIVF, 1186 arm.ADIVD, 1187 obj.ACHECKNIL, 1188 /* read */ 1189 arm.ACMPF, /* read, read, */ 1190 arm.ACMPD, 1191 arm.ACMP, 1192 arm.ACMN, 1193 arm.ATST: 1194 /* read,, */ 1195 if s != nil { 1196 if copysub(&p.From, v, s, true) { 1197 return 1 1198 } 1199 if copysub1(p, v, s, true) { 1200 return 1 1201 } 1202 if !copyas(&p.To, v) { 1203 if copysub(&p.To, v, s, true) { 1204 return 1 1205 } 1206 } 1207 return 0 1208 } 1209 1210 if copyas(&p.To, v) { 1211 if p.Scond != arm.C_SCOND_NONE { 1212 return 2 1213 } 1214 if p.Reg == 0 { 1215 p.Reg = p.To.Reg 1216 } 1217 if copyau(&p.From, v) { 1218 return 4 1219 } 1220 if copyau1(p, v) { 1221 return 4 1222 } 1223 return 3 1224 } 1225 1226 if copyau(&p.From, v) { 1227 return 1 1228 } 1229 if copyau1(p, v) { 1230 return 1 1231 } 1232 if copyau(&p.To, v) { 1233 return 1 1234 } 1235 return 0 1236 1237 case arm.ABEQ, /* read, read */ 1238 arm.ABNE, 1239 arm.ABCS, 1240 arm.ABHS, 1241 arm.ABCC, 1242 arm.ABLO, 1243 arm.ABMI, 1244 arm.ABPL, 1245 arm.ABVS, 1246 arm.ABVC, 1247 arm.ABHI, 1248 arm.ABLS, 1249 arm.ABGE, 1250 arm.ABLT, 1251 arm.ABGT, 1252 arm.ABLE: 1253 if s != nil { 1254 if copysub(&p.From, v, s, true) { 1255 return 1 1256 } 1257 if copysub1(p, v, s, true) { 1258 return 1 1259 } 1260 return 0 1261 } 1262 1263 if copyau(&p.From, v) { 1264 return 1 1265 } 1266 if copyau1(p, v) { 1267 return 1 1268 } 1269 return 0 1270 1271 case arm.AB: /* funny */ 1272 if s != nil { 1273 if copysub(&p.To, v, s, true) { 1274 return 1 1275 } 1276 return 0 1277 } 1278 if copyau(&p.To, v) { 1279 return 1 1280 } 1281 return 0 1282 1283 case obj.ARET: /* funny */ 1284 if s != nil { 1285 return 1 1286 } 1287 return 3 1288 1289 case arm.ABL: /* funny */ 1290 if v.Type == obj.TYPE_REG { 1291 // TODO(rsc): REG_R0 and REG_F0 used to be 1292 // (when register numbers started at 0) exregoffset and exfregoffset, 1293 // which are unset entirely. 1294 // It's strange that this handles R0 and F0 differently from the other 1295 // registers. Possible failure to optimize? 1296 if arm.REG_R0 < v.Reg && v.Reg <= arm.REGEXT { 1297 return 2 1298 } 1299 if v.Reg == arm.REGARG { 1300 return 2 1301 } 1302 if arm.REG_F0 < v.Reg && v.Reg <= arm.FREGEXT { 1303 return 2 1304 } 1305 } 1306 1307 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { 1308 return 2 1309 } 1310 1311 if s != nil { 1312 if copysub(&p.To, v, s, true) { 1313 return 1 1314 } 1315 return 0 1316 } 1317 1318 if copyau(&p.To, v) { 1319 return 4 1320 } 1321 return 3 1322 1323 // R0 is zero, used by DUFFZERO, cannot be substituted. 1324 // R1 is ptr to memory, used and set, cannot be substituted. 1325 case obj.ADUFFZERO: 1326 if v.Type == obj.TYPE_REG { 1327 if v.Reg == arm.REG_R0 { 1328 return 1 1329 } 1330 if v.Reg == arm.REG_R0+1 { 1331 return 2 1332 } 1333 } 1334 1335 return 0 1336 1337 // R0 is scratch, set by DUFFCOPY, cannot be substituted. 1338 // R1, R2 areptr to src, dst, used and set, cannot be substituted. 1339 case obj.ADUFFCOPY: 1340 if v.Type == obj.TYPE_REG { 1341 if v.Reg == arm.REG_R0 { 1342 return 3 1343 } 1344 if v.Reg == arm.REG_R0+1 || v.Reg == arm.REG_R0+2 { 1345 return 2 1346 } 1347 } 1348 1349 return 0 1350 1351 case obj.ATEXT: /* funny */ 1352 if v.Type == obj.TYPE_REG { 1353 if v.Reg == arm.REGARG { 1354 return 3 1355 } 1356 } 1357 return 0 1358 1359 case obj.APCDATA, 1360 obj.AFUNCDATA, 1361 obj.AVARDEF, 1362 obj.AVARKILL, 1363 obj.AVARLIVE, 1364 obj.AUSEFIELD: 1365 return 0 1366 } 1367 } 1368 1369 /* 1370 * direct reference, 1371 * could be set/use depending on 1372 * semantics 1373 */ 1374 func copyas(a *obj.Addr, v *obj.Addr) bool { 1375 if regtyp(v) { 1376 if a.Type == v.Type { 1377 if a.Reg == v.Reg { 1378 return true 1379 } 1380 } 1381 } else if v.Type == obj.TYPE_CONST { /* for constprop */ 1382 if a.Type == v.Type { 1383 if a.Name == v.Name { 1384 if a.Sym == v.Sym { 1385 if a.Reg == v.Reg { 1386 if a.Offset == v.Offset { 1387 return true 1388 } 1389 } 1390 } 1391 } 1392 } 1393 } 1394 1395 return false 1396 } 1397 1398 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 1399 if a.Type != v.Type { 1400 return false 1401 } 1402 if regtyp(v) && a.Reg == v.Reg { 1403 return true 1404 } 1405 1406 // TODO(rsc): Change v->type to v->name and enable. 1407 //if(v->type == NAME_AUTO || v->type == NAME_PARAM) { 1408 // if(v->offset == a->offset) 1409 // return 1; 1410 //} 1411 return false 1412 } 1413 1414 /* 1415 * either direct or indirect 1416 */ 1417 func copyau(a *obj.Addr, v *obj.Addr) bool { 1418 if copyas(a, v) { 1419 return true 1420 } 1421 if v.Type == obj.TYPE_REG { 1422 if a.Type == obj.TYPE_ADDR && a.Reg != 0 { 1423 if a.Reg == v.Reg { 1424 return true 1425 } 1426 } else if a.Type == obj.TYPE_MEM { 1427 if a.Reg == v.Reg { 1428 return true 1429 } 1430 } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { 1431 if a.Reg == v.Reg { 1432 return true 1433 } 1434 if a.Offset == int64(v.Reg) { 1435 return true 1436 } 1437 } else if a.Type == obj.TYPE_SHIFT { 1438 if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { 1439 return true 1440 } 1441 if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { 1442 return true 1443 } 1444 } 1445 } 1446 1447 return false 1448 } 1449 1450 /* 1451 * compare v to the center 1452 * register in p (p->reg) 1453 */ 1454 func copyau1(p *obj.Prog, v *obj.Addr) bool { 1455 if v.Type == obj.TYPE_REG && v.Reg == 0 { 1456 return false 1457 } 1458 return p.Reg == v.Reg 1459 } 1460 1461 // copysub substitute s for v in a. 1462 // copysub returns true on failure to substitute. 1463 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. 1464 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { 1465 if f && copyau(a, v) { 1466 if a.Type == obj.TYPE_SHIFT { 1467 if a.Offset&0xf == int64(v.Reg-arm.REG_R0) { 1468 a.Offset = a.Offset&^0xf | int64(s.Reg)&0xf 1469 } 1470 if (a.Offset&(1<<4) != 0) && (a.Offset>>8)&0xf == int64(v.Reg-arm.REG_R0) { 1471 a.Offset = a.Offset&^(0xf<<8) | (int64(s.Reg)&0xf)<<8 1472 } 1473 } else if a.Type == obj.TYPE_REGREG || a.Type == obj.TYPE_REGREG2 { 1474 if a.Offset == int64(v.Reg) { 1475 a.Offset = int64(s.Reg) 1476 } 1477 if a.Reg == v.Reg { 1478 a.Reg = s.Reg 1479 } 1480 } else { 1481 a.Reg = s.Reg 1482 } 1483 } 1484 return false 1485 } 1486 1487 // TODO(dfc) remove unused return value, remove calls with f=false as they do nothing. 1488 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f bool) bool { 1489 if f && copyau1(p1, v) { 1490 p1.Reg = s.Reg 1491 } 1492 return false 1493 } 1494 1495 var predinfo = []struct { 1496 opcode obj.As 1497 notopcode obj.As 1498 scond int 1499 notscond int 1500 }{ 1501 {arm.ABEQ, arm.ABNE, 0x0, 0x1}, 1502 {arm.ABNE, arm.ABEQ, 0x1, 0x0}, 1503 {arm.ABCS, arm.ABCC, 0x2, 0x3}, 1504 {arm.ABHS, arm.ABLO, 0x2, 0x3}, 1505 {arm.ABCC, arm.ABCS, 0x3, 0x2}, 1506 {arm.ABLO, arm.ABHS, 0x3, 0x2}, 1507 {arm.ABMI, arm.ABPL, 0x4, 0x5}, 1508 {arm.ABPL, arm.ABMI, 0x5, 0x4}, 1509 {arm.ABVS, arm.ABVC, 0x6, 0x7}, 1510 {arm.ABVC, arm.ABVS, 0x7, 0x6}, 1511 {arm.ABHI, arm.ABLS, 0x8, 0x9}, 1512 {arm.ABLS, arm.ABHI, 0x9, 0x8}, 1513 {arm.ABGE, arm.ABLT, 0xA, 0xB}, 1514 {arm.ABLT, arm.ABGE, 0xB, 0xA}, 1515 {arm.ABGT, arm.ABLE, 0xC, 0xD}, 1516 {arm.ABLE, arm.ABGT, 0xD, 0xC}, 1517 } 1518 1519 type Joininfo struct { 1520 start *gc.Flow 1521 last *gc.Flow 1522 end *gc.Flow 1523 len int 1524 } 1525 1526 const ( 1527 Join = iota 1528 Split 1529 End 1530 Branch 1531 Setcond 1532 Toolong 1533 ) 1534 1535 const ( 1536 Falsecond = iota 1537 Truecond 1538 Delbranch 1539 Keepbranch 1540 ) 1541 1542 func isbranch(p *obj.Prog) bool { 1543 return (arm.ABEQ <= p.As) && (p.As <= arm.ABLE) 1544 } 1545 1546 func predicable(p *obj.Prog) bool { 1547 switch p.As { 1548 case obj.ANOP, 1549 obj.AXXX, 1550 obj.AGLOBL, 1551 obj.ATEXT, 1552 arm.AWORD: 1553 return false 1554 } 1555 1556 if isbranch(p) { 1557 return false 1558 } 1559 return true 1560 } 1561 1562 /* 1563 * Depends on an analysis of the encodings performed by 5l. 1564 * These seem to be all of the opcodes that lead to the "S" bit 1565 * being set in the instruction encodings. 1566 * 1567 * C_SBIT may also have been set explicitly in p->scond. 1568 */ 1569 func modifiescpsr(p *obj.Prog) bool { 1570 switch p.As { 1571 case arm.AMULLU, 1572 arm.AMULA, 1573 arm.AMULU, 1574 arm.ADIVU, 1575 arm.ATEQ, 1576 arm.ACMN, 1577 arm.ATST, 1578 arm.ACMP, 1579 arm.AMUL, 1580 arm.ADIV, 1581 arm.AMOD, 1582 arm.AMODU, 1583 arm.ABL: 1584 return true 1585 } 1586 1587 if p.Scond&arm.C_SBIT != 0 { 1588 return true 1589 } 1590 return false 1591 } 1592 1593 /* 1594 * Find the maximal chain of instructions starting with r which could 1595 * be executed conditionally 1596 */ 1597 func joinsplit(r *gc.Flow, j *Joininfo) int { 1598 j.start = r 1599 j.last = r 1600 j.len = 0 1601 for { 1602 if r.P2 != nil && (r.P1 != nil || r.P2.P2link != nil) { 1603 j.end = r 1604 return Join 1605 } 1606 1607 if r.S1 != nil && r.S2 != nil { 1608 j.end = r 1609 return Split 1610 } 1611 1612 j.last = r 1613 if r.Prog.As != obj.ANOP { 1614 j.len++ 1615 } 1616 if r.S1 == nil && r.S2 == nil { 1617 j.end = r.Link 1618 return End 1619 } 1620 1621 if r.S2 != nil { 1622 j.end = r.S2 1623 return Branch 1624 } 1625 1626 if modifiescpsr(r.Prog) { 1627 j.end = r.S1 1628 return Setcond 1629 } 1630 1631 r = r.S1 1632 if j.len >= 4 { 1633 break 1634 } 1635 } 1636 1637 j.end = r 1638 return Toolong 1639 } 1640 1641 func successor(r *gc.Flow) *gc.Flow { 1642 if r.S1 != nil { 1643 return r.S1 1644 } 1645 return r.S2 1646 } 1647 1648 func applypred(rstart *gc.Flow, j *Joininfo, cond int, branch int) { 1649 if j.len == 0 { 1650 return 1651 } 1652 var pred int 1653 if cond == Truecond { 1654 pred = predinfo[rstart.Prog.As-arm.ABEQ].scond 1655 } else { 1656 pred = predinfo[rstart.Prog.As-arm.ABEQ].notscond 1657 } 1658 1659 for r := j.start; ; r = successor(r) { 1660 if r.Prog.As == arm.AB { 1661 if r != j.last || branch == Delbranch { 1662 excise(r) 1663 } else { 1664 if cond == Truecond { 1665 r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].opcode 1666 } else { 1667 r.Prog.As = predinfo[rstart.Prog.As-arm.ABEQ].notopcode 1668 } 1669 } 1670 } else if predicable(r.Prog) { 1671 r.Prog.Scond = uint8(int(r.Prog.Scond&^arm.C_SCOND) | pred) 1672 } 1673 if r.S1 != r.Link { 1674 r.S1 = r.Link 1675 r.Link.P1 = r 1676 } 1677 1678 if r == j.last { 1679 break 1680 } 1681 } 1682 } 1683 1684 func predicate(g *gc.Graph) { 1685 var t1 int 1686 var t2 int 1687 var j1 Joininfo 1688 var j2 Joininfo 1689 1690 for r := g.Start; r != nil; r = r.Link { 1691 if isbranch(r.Prog) { 1692 t1 = joinsplit(r.S1, &j1) 1693 t2 = joinsplit(r.S2, &j2) 1694 if j1.last.Link != j2.start { 1695 continue 1696 } 1697 if j1.end == j2.end { 1698 if (t1 == Branch && (t2 == Join || t2 == Setcond)) || (t2 == Join && (t1 == Join || t1 == Setcond)) { 1699 applypred(r, &j1, Falsecond, Delbranch) 1700 applypred(r, &j2, Truecond, Delbranch) 1701 excise(r) 1702 continue 1703 } 1704 } 1705 1706 if t1 == End || t1 == Branch { 1707 applypred(r, &j1, Falsecond, Keepbranch) 1708 excise(r) 1709 continue 1710 } 1711 } 1712 } 1713 } 1714 1715 func isdconst(a *obj.Addr) bool { 1716 return a.Type == obj.TYPE_CONST 1717 } 1718 1719 func isfloatreg(a *obj.Addr) bool { 1720 return arm.REG_F0 <= a.Reg && a.Reg <= arm.REG_F15 1721 } 1722 1723 func stackaddr(a *obj.Addr) bool { 1724 return regtyp(a) && a.Reg == arm.REGSP 1725 } 1726 1727 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 1728 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 1729 } 1730 1731 func excise(r *gc.Flow) { 1732 p := r.Prog 1733 obj.Nopout(p) 1734 }