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