github.com/euank/go@v0.0.0-20160829210321-495514729181/src/cmd/compile/internal/amd64/peep.go (about) 1 // Derived from Inferno utils/6c/peep.c 2 // https://bitbucket.org/inferno-os/inferno-os/src/default/utils/6c/peep.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 package amd64 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/x86" 37 "fmt" 38 ) 39 40 var gactive uint32 41 42 const ( 43 exregoffset = x86.REG_R15 44 ) 45 46 // do we need the carry bit 47 func needc(p *obj.Prog) bool { 48 for p != nil { 49 flags := progcarryflags(p) 50 if flags&gc.UseCarry != 0 { 51 return true 52 } 53 if 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 x86.ALEAQ: 102 if regtyp(&p.To) { 103 if p.From.Sym != nil { 104 if p.From.Index == x86.REG_NONE { 105 conprop(r) 106 } 107 } 108 } 109 110 case x86.AMOVB, 111 x86.AMOVW, 112 x86.AMOVL, 113 x86.AMOVQ, 114 x86.AMOVSS, 115 x86.AMOVSD: 116 if regtyp(&p.To) { 117 if p.From.Type == obj.TYPE_CONST || p.From.Type == obj.TYPE_FCONST { 118 conprop(r) 119 } 120 } 121 } 122 } 123 124 var r *gc.Flow 125 var r1 *gc.Flow 126 var p1 *obj.Prog 127 var t int 128 loop1: 129 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 130 gc.Dumpit("loop1", g.Start, 0) 131 } 132 133 t = 0 134 for r = g.Start; r != nil; r = r.Link { 135 p = r.Prog 136 switch p.As { 137 case x86.AMOVL, 138 x86.AMOVQ, 139 x86.AMOVSS, 140 x86.AMOVSD: 141 if regtyp(&p.To) { 142 if regtyp(&p.From) { 143 if copyprop(g, r) { 144 excise(r) 145 t++ 146 } else if subprop(r) && copyprop(g, r) { 147 excise(r) 148 t++ 149 } 150 } 151 } 152 153 case x86.AMOVBLZX, 154 x86.AMOVWLZX, 155 x86.AMOVBLSX, 156 x86.AMOVWLSX: 157 if regtyp(&p.To) { 158 r1 = rnops(gc.Uniqs(r)) 159 if r1 != nil { 160 p1 = r1.Prog 161 if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { 162 p1.As = x86.AMOVL 163 t++ 164 } 165 } 166 } 167 168 case x86.AMOVBQSX, 169 x86.AMOVBQZX, 170 x86.AMOVWQSX, 171 x86.AMOVWQZX, 172 x86.AMOVLQSX, 173 x86.AMOVLQZX, 174 x86.AMOVQL: 175 if regtyp(&p.To) { 176 r1 = rnops(gc.Uniqs(r)) 177 if r1 != nil { 178 p1 = r1.Prog 179 if p.As == p1.As && p.To.Type == p1.From.Type && p.To.Reg == p1.From.Reg { 180 p1.As = x86.AMOVQ 181 t++ 182 } 183 } 184 } 185 186 case x86.AADDL, 187 x86.AADDQ, 188 x86.AADDW: 189 if p.From.Type != obj.TYPE_CONST || needc(p.Link) { 190 break 191 } 192 if p.From.Offset == -1 { 193 if p.As == x86.AADDQ { 194 p.As = x86.ADECQ 195 } else if p.As == x86.AADDL { 196 p.As = x86.ADECL 197 } else { 198 p.As = x86.ADECW 199 } 200 p.From = obj.Addr{} 201 break 202 } 203 204 if p.From.Offset == 1 { 205 if p.As == x86.AADDQ { 206 p.As = x86.AINCQ 207 } else if p.As == x86.AADDL { 208 p.As = x86.AINCL 209 } else { 210 p.As = x86.AINCW 211 } 212 p.From = obj.Addr{} 213 break 214 } 215 216 case x86.ASUBL, 217 x86.ASUBQ, 218 x86.ASUBW: 219 if p.From.Type != obj.TYPE_CONST || needc(p.Link) { 220 break 221 } 222 if p.From.Offset == -1 { 223 if p.As == x86.ASUBQ { 224 p.As = x86.AINCQ 225 } else if p.As == x86.ASUBL { 226 p.As = x86.AINCL 227 } else { 228 p.As = x86.AINCW 229 } 230 p.From = obj.Addr{} 231 break 232 } 233 234 if p.From.Offset == 1 { 235 if p.As == x86.ASUBQ { 236 p.As = x86.ADECQ 237 } else if p.As == x86.ASUBL { 238 p.As = x86.ADECL 239 } else { 240 p.As = x86.ADECW 241 } 242 p.From = obj.Addr{} 243 break 244 } 245 } 246 } 247 248 if t != 0 { 249 goto loop1 250 } 251 252 // MOVLQZX removal. 253 // The MOVLQZX exists to avoid being confused for a 254 // MOVL that is just copying 32-bit data around during 255 // copyprop. Now that copyprop is done, remov MOVLQZX R1, R2 256 // if it is dominated by an earlier ADDL/MOVL/etc into R1 that 257 // will have already cleared the high bits. 258 // 259 // MOVSD removal. 260 // We never use packed registers, so a MOVSD between registers 261 // can be replaced by MOVAPD, which moves the pair of float64s 262 // instead of just the lower one. We only use the lower one, but 263 // the processor can do better if we do moves using both. 264 for r := g.Start; r != nil; r = r.Link { 265 p = r.Prog 266 if p.As == x86.AMOVLQZX { 267 if regtyp(&p.From) { 268 if p.From.Type == p.To.Type && p.From.Reg == p.To.Reg { 269 if prevl(r, p.From.Reg) { 270 excise(r) 271 } 272 } 273 } 274 } 275 276 if p.As == x86.AMOVSD { 277 if regtyp(&p.From) { 278 if regtyp(&p.To) { 279 p.As = x86.AMOVAPD 280 } 281 } 282 } 283 } 284 285 // load pipelining 286 // push any load from memory as early as possible 287 // to give it time to complete before use. 288 for r := g.Start; r != nil; r = r.Link { 289 p = r.Prog 290 switch p.As { 291 case x86.AMOVB, 292 x86.AMOVW, 293 x86.AMOVL, 294 x86.AMOVQ, 295 x86.AMOVLQZX: 296 if regtyp(&p.To) && !regconsttyp(&p.From) { 297 pushback(r) 298 } 299 } 300 } 301 302 gc.Flowend(g) 303 } 304 305 func pushback(r0 *gc.Flow) { 306 var r *gc.Flow 307 var p *obj.Prog 308 309 var b *gc.Flow 310 p0 := r0.Prog 311 for r = gc.Uniqp(r0); r != nil && gc.Uniqs(r) != nil; r = gc.Uniqp(r) { 312 p = r.Prog 313 if p.As != obj.ANOP { 314 if !regconsttyp(&p.From) || !regtyp(&p.To) { 315 break 316 } 317 if copyu(p, &p0.To, nil) != 0 || copyu(p0, &p.To, nil) != 0 { 318 break 319 } 320 } 321 322 if p.As == obj.ACALL { 323 break 324 } 325 b = r 326 } 327 328 if b == nil { 329 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 330 fmt.Printf("no pushback: %v\n", r0.Prog) 331 if r != nil { 332 fmt.Printf("\t%v [%v]\n", r.Prog, gc.Uniqs(r) != nil) 333 } 334 } 335 336 return 337 } 338 339 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 340 fmt.Printf("pushback\n") 341 for r := b; ; r = r.Link { 342 fmt.Printf("\t%v\n", r.Prog) 343 if r == r0 { 344 break 345 } 346 } 347 } 348 349 t := *r0.Prog 350 for r = gc.Uniqp(r0); ; r = gc.Uniqp(r) { 351 p0 = r.Link.Prog 352 p = r.Prog 353 p0.As = p.As 354 p0.Lineno = p.Lineno 355 p0.From = p.From 356 p0.To = p.To 357 358 if r == b { 359 break 360 } 361 } 362 363 p0 = r.Prog 364 p0.As = t.As 365 p0.Lineno = t.Lineno 366 p0.From = t.From 367 p0.To = t.To 368 369 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 370 fmt.Printf("\tafter\n") 371 for r := b; ; r = r.Link { 372 fmt.Printf("\t%v\n", r.Prog) 373 if r == r0 { 374 break 375 } 376 } 377 } 378 } 379 380 func excise(r *gc.Flow) { 381 p := r.Prog 382 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 383 fmt.Printf("%v ===delete===\n", p) 384 } 385 386 obj.Nopout(p) 387 388 gc.Ostats.Ndelmov++ 389 } 390 391 func regtyp(a *obj.Addr) bool { 392 return a.Type == obj.TYPE_REG && (x86.REG_AX <= a.Reg && a.Reg <= x86.REG_R15 || x86.REG_X0 <= a.Reg && a.Reg <= x86.REG_X15) 393 } 394 395 // movb elimination. 396 // movb is simulated by the linker 397 // when a register other than ax, bx, cx, dx 398 // is used, so rewrite to other instructions 399 // when possible. a movb into a register 400 // can smash the entire 32-bit register without 401 // causing any trouble. 402 // 403 // TODO: Using the Q forms here instead of the L forms 404 // seems unnecessary, and it makes the instructions longer. 405 func elimshortmov(g *gc.Graph) { 406 var p *obj.Prog 407 408 for r := g.Start; r != nil; r = r.Link { 409 p = r.Prog 410 if regtyp(&p.To) { 411 switch p.As { 412 case x86.AINCB, 413 x86.AINCW: 414 p.As = x86.AINCQ 415 416 case x86.ADECB, 417 x86.ADECW: 418 p.As = x86.ADECQ 419 420 case x86.ANEGB, 421 x86.ANEGW: 422 p.As = x86.ANEGQ 423 424 case x86.ANOTB, 425 x86.ANOTW: 426 p.As = x86.ANOTQ 427 } 428 429 if regtyp(&p.From) || p.From.Type == obj.TYPE_CONST { 430 // move or arithmetic into partial register. 431 // from another register or constant can be movl. 432 // we don't switch to 64-bit arithmetic if it can 433 // change how the carry bit is set (and the carry bit is needed). 434 switch p.As { 435 case x86.AMOVB, 436 x86.AMOVW: 437 p.As = x86.AMOVQ 438 439 case x86.AADDB, 440 x86.AADDW: 441 if !needc(p.Link) { 442 p.As = x86.AADDQ 443 } 444 445 case x86.ASUBB, 446 x86.ASUBW: 447 if !needc(p.Link) { 448 p.As = x86.ASUBQ 449 } 450 451 case x86.AMULB, 452 x86.AMULW: 453 p.As = x86.AMULQ 454 455 case x86.AIMULB, 456 x86.AIMULW: 457 p.As = x86.AIMULQ 458 459 case x86.AANDB, 460 x86.AANDW: 461 p.As = x86.AANDQ 462 463 case x86.AORB, 464 x86.AORW: 465 p.As = x86.AORQ 466 467 case x86.AXORB, 468 x86.AXORW: 469 p.As = x86.AXORQ 470 471 case x86.ASHLB, 472 x86.ASHLW: 473 p.As = x86.ASHLQ 474 } 475 } else if p.From.Type != obj.TYPE_REG { 476 // explicit zero extension, but don't 477 // do that if source is a byte register 478 // (only AH can occur and it's forbidden). 479 switch p.As { 480 case x86.AMOVB: 481 p.As = x86.AMOVBQZX 482 483 case x86.AMOVW: 484 p.As = x86.AMOVWQZX 485 } 486 } 487 } 488 } 489 } 490 491 // is 'a' a register or constant? 492 func regconsttyp(a *obj.Addr) bool { 493 if regtyp(a) { 494 return true 495 } 496 switch a.Type { 497 case obj.TYPE_CONST, 498 obj.TYPE_FCONST, 499 obj.TYPE_SCONST, 500 obj.TYPE_ADDR: // TODO(rsc): Not all TYPE_ADDRs are constants. 501 return true 502 } 503 504 return false 505 } 506 507 // is reg guaranteed to be truncated by a previous L instruction? 508 func prevl(r0 *gc.Flow, reg int16) bool { 509 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 510 p := r.Prog 511 if p.To.Type == obj.TYPE_REG && p.To.Reg == reg { 512 flags := progflags(p) 513 if flags&gc.RightWrite != 0 { 514 if flags&gc.SizeL != 0 { 515 return true 516 } 517 return false 518 } 519 } 520 } 521 return false 522 } 523 524 /* 525 * the idea is to substitute 526 * one register for another 527 * from one MOV to another 528 * MOV a, R0 529 * ADD b, R0 / no use of R1 530 * MOV R0, R1 531 * would be converted to 532 * MOV a, R1 533 * ADD b, R1 534 * MOV R1, R0 535 * hopefully, then the former or latter MOV 536 * will be eliminated by copy propagation. 537 */ 538 func subprop(r0 *gc.Flow) bool { 539 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 540 fmt.Printf("subprop %v\n", r0.Prog) 541 } 542 p := r0.Prog 543 v1 := &p.From 544 if !regtyp(v1) { 545 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 546 fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) 547 } 548 return false 549 } 550 551 v2 := &p.To 552 if !regtyp(v2) { 553 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 554 fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) 555 } 556 return false 557 } 558 559 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 560 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 561 fmt.Printf("\t? %v\n", r.Prog) 562 } 563 if gc.Uniqs(r) == nil { 564 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 565 fmt.Printf("\tno unique successor\n") 566 } 567 break 568 } 569 570 p = r.Prog 571 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 572 continue 573 } 574 if p.Info.Flags&gc.Call != 0 { 575 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 576 fmt.Printf("\tfound %v; return 0\n", p) 577 } 578 return false 579 } 580 581 if p.Info.Reguse|p.Info.Regset != 0 { 582 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 583 fmt.Printf("\tfound %v; return 0\n", p) 584 } 585 return false 586 } 587 588 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 { 589 copysub(&p.To, v1, v2, true) 590 if gc.Debug['P'] != 0 { 591 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 592 if p.From.Type == v2.Type && p.From.Reg == v2.Reg { 593 fmt.Printf(" excise") 594 } 595 fmt.Printf("\n") 596 } 597 598 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 599 p = r.Prog 600 copysub(&p.From, v1, v2, true) 601 copysub(&p.To, v1, v2, true) 602 if gc.Debug['P'] != 0 { 603 fmt.Printf("%v\n", r.Prog) 604 } 605 } 606 607 v1.Reg, v2.Reg = v2.Reg, v1.Reg 608 if gc.Debug['P'] != 0 { 609 fmt.Printf("%v last\n", r.Prog) 610 } 611 return true 612 } 613 614 if copyau(&p.From, v2) || copyau(&p.To, v2) { 615 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 616 fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) 617 } 618 break 619 } 620 621 if copysub(&p.From, v1, v2, false) || copysub(&p.To, v1, v2, false) { 622 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 623 fmt.Printf("\tcopysub failed\n") 624 } 625 break 626 } 627 } 628 629 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 630 fmt.Printf("\tran off end; return 0\n") 631 } 632 return false 633 } 634 635 /* 636 * The idea is to remove redundant copies. 637 * v1->v2 F=0 638 * (use v2 s/v2/v1/)* 639 * set v1 F=1 640 * use v2 return fail 641 * ----------------- 642 * v1->v2 F=0 643 * (use v2 s/v2/v1/)* 644 * set v1 F=1 645 * set v2 return success 646 */ 647 func copyprop(g *gc.Graph, r0 *gc.Flow) bool { 648 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 649 fmt.Printf("copyprop %v\n", r0.Prog) 650 } 651 p := r0.Prog 652 v1 := &p.From 653 v2 := &p.To 654 if copyas(v1, v2) { 655 return true 656 } 657 gactive++ 658 return copy1(v1, v2, r0.S1, false) 659 } 660 661 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f bool) bool { 662 if uint32(r.Active) == gactive { 663 if gc.Debug['P'] != 0 { 664 fmt.Printf("act set; return 1\n") 665 } 666 return true 667 } 668 669 r.Active = int32(gactive) 670 if gc.Debug['P'] != 0 { 671 fmt.Printf("copy %v->%v f=%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 672 } 673 for ; r != nil; r = r.S1 { 674 p := r.Prog 675 if gc.Debug['P'] != 0 { 676 fmt.Printf("%v", p) 677 } 678 if !f && gc.Uniqp(r) == nil { 679 f = true 680 if gc.Debug['P'] != 0 { 681 fmt.Printf("; merge; f=%v", f) 682 } 683 } 684 685 switch t := copyu(p, v2, nil); t { 686 case 2: /* rar, can't split */ 687 if gc.Debug['P'] != 0 { 688 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 689 } 690 return false 691 692 case 3: /* set */ 693 if gc.Debug['P'] != 0 { 694 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 695 } 696 return true 697 698 case 1, /* used, substitute */ 699 4: /* use and set */ 700 if f { 701 if gc.Debug['P'] == 0 { 702 return false 703 } 704 if t == 4 { 705 fmt.Printf("; %v used+set and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 706 } else { 707 fmt.Printf("; %v used and f=%v; return 0\n", gc.Ctxt.Dconv(v2), f) 708 } 709 return false 710 } 711 712 if copyu(p, v2, v1) != 0 { 713 if gc.Debug['P'] != 0 { 714 fmt.Printf("; sub fail; return 0\n") 715 } 716 return false 717 } 718 719 if gc.Debug['P'] != 0 { 720 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 721 } 722 if t == 4 { 723 if gc.Debug['P'] != 0 { 724 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 725 } 726 return true 727 } 728 } 729 730 if !f { 731 t := copyu(p, v1, nil) 732 if t == 2 || t == 3 || t == 4 { 733 f = true 734 if gc.Debug['P'] != 0 { 735 fmt.Printf("; %v set and !f; f=%v", gc.Ctxt.Dconv(v1), f) 736 } 737 } 738 } 739 740 if gc.Debug['P'] != 0 { 741 fmt.Printf("\n") 742 } 743 if r.S2 != nil { 744 if !copy1(v1, v2, r.S2, f) { 745 return false 746 } 747 } 748 } 749 return true 750 } 751 752 /* 753 * return 754 * 1 if v only used (and substitute), 755 * 2 if read-alter-rewrite 756 * 3 if set 757 * 4 if set and used 758 * 0 otherwise (not touched) 759 */ 760 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 761 switch p.As { 762 case obj.AJMP: 763 if s != nil { 764 if copysub(&p.To, v, s, true) { 765 return 1 766 } 767 return 0 768 } 769 770 if copyau(&p.To, v) { 771 return 1 772 } 773 return 0 774 775 case obj.ARET: 776 if s != nil { 777 return 1 778 } 779 return 3 780 781 case obj.ACALL: 782 if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset { 783 return 2 784 } 785 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 786 return 2 787 } 788 if v.Type == p.From.Type && v.Reg == p.From.Reg { 789 return 2 790 } 791 792 if s != nil { 793 if copysub(&p.To, v, s, true) { 794 return 1 795 } 796 return 0 797 } 798 799 if copyau(&p.To, v) { 800 return 4 801 } 802 return 3 803 804 case obj.ATEXT: 805 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 806 return 3 807 } 808 return 0 809 } 810 811 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 812 return 0 813 } 814 815 if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { 816 return 2 817 } 818 819 if (p.Info.Reguse|p.Info.Regset)&FtoB(int(v.Reg)) != 0 { 820 return 2 821 } 822 823 if p.Info.Flags&gc.LeftAddr != 0 { 824 if copyas(&p.From, v) { 825 return 2 826 } 827 } 828 829 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { 830 if copyas(&p.To, v) { 831 return 2 832 } 833 } 834 835 if p.Info.Flags&gc.RightWrite != 0 { 836 if copyas(&p.To, v) { 837 if s != nil { 838 if copysub(&p.From, v, s, true) { 839 return 1 840 } 841 return 0 842 } 843 if copyau(&p.From, v) { 844 return 4 845 } 846 return 3 847 } 848 } 849 850 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { 851 if s != nil { 852 if copysub(&p.From, v, s, true) { 853 return 1 854 } 855 if copysub(&p.To, v, s, true) { 856 return 1 857 } 858 return 0 859 } 860 861 if copyau(&p.From, v) { 862 return 1 863 } 864 if copyau(&p.To, v) { 865 return 1 866 } 867 } 868 return 0 869 } 870 871 /* 872 * direct reference, 873 * could be set/use depending on 874 * semantics 875 */ 876 func copyas(a *obj.Addr, v *obj.Addr) bool { 877 if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B { 878 gc.Fatalf("use of byte register") 879 } 880 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B { 881 gc.Fatalf("use of byte register") 882 } 883 884 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 885 return false 886 } 887 if regtyp(v) { 888 return true 889 } 890 if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 891 if v.Offset == a.Offset { 892 return true 893 } 894 } 895 return false 896 } 897 898 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 899 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 900 return false 901 } 902 if regtyp(v) { 903 return true 904 } 905 if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 906 if v.Offset == a.Offset { 907 return true 908 } 909 } 910 return false 911 } 912 913 /* 914 * either direct or indirect 915 */ 916 func copyau(a *obj.Addr, v *obj.Addr) bool { 917 if copyas(a, v) { 918 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 919 fmt.Printf("\tcopyau: copyas returned 1\n") 920 } 921 return true 922 } 923 924 if regtyp(v) { 925 if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { 926 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 927 fmt.Printf("\tcopyau: found indir use - return 1\n") 928 } 929 return true 930 } 931 932 if a.Index == v.Reg { 933 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 934 fmt.Printf("\tcopyau: found index use - return 1\n") 935 } 936 return true 937 } 938 } 939 return false 940 } 941 942 // copysub substitute s for v in a. 943 // copysub returns true on failure to substitute. TODO(dfc) reverse this logic, copysub should return false on failure 944 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f bool) bool { 945 if copyas(a, v) { 946 if s.Reg >= x86.REG_AX && s.Reg <= x86.REG_R15 || s.Reg >= x86.REG_X0 && s.Reg <= x86.REG_X0+15 { 947 if f { 948 a.Reg = s.Reg 949 } 950 } 951 return false 952 } 953 954 if regtyp(v) { 955 if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { 956 if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { 957 return true /* can't use BP-base with index */ 958 } 959 if f { 960 a.Reg = s.Reg 961 } 962 } 963 if a.Index == v.Reg { 964 if f { 965 a.Index = s.Reg 966 } 967 } 968 } 969 return false 970 } 971 972 func conprop(r0 *gc.Flow) { 973 p0 := r0.Prog 974 v0 := &p0.To 975 r := r0 976 977 loop: 978 r = gc.Uniqs(r) 979 if r == nil || r == r0 { 980 return 981 } 982 if gc.Uniqp(r) == nil { 983 return 984 } 985 986 p := r.Prog 987 t := copyu(p, v0, nil) 988 switch t { 989 case 0, // miss 990 1: // use 991 goto loop 992 993 case 2, // rar 994 4: // use and set 995 break 996 997 case 3: // set 998 if p.As == p0.As { 999 if p.From.Type == p0.From.Type { 1000 if p.From.Reg == p0.From.Reg { 1001 if p.From.Node == p0.From.Node { 1002 if p.From.Offset == p0.From.Offset { 1003 if p.From.Scale == p0.From.Scale { 1004 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { 1005 if p.From.Index == p0.From.Index { 1006 excise(r) 1007 goto loop 1008 } 1009 } 1010 } 1011 } 1012 } 1013 } 1014 } 1015 } 1016 } 1017 } 1018 1019 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 1020 return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 1021 } 1022 1023 func stackaddr(a *obj.Addr) bool { 1024 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP 1025 }