github.com/sean-/go@v0.0.0-20151219100004-97f854cd7bb6/src/cmd/compile/internal/amd64/peep.go (about) 1 // Derived from Inferno utils/6c/peep.c 2 // http://code.google.com/p/inferno-os/source/browse/utils/6c/peep.c 3 // 4 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 5 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 6 // Portions Copyright © 1997-1999 Vita Nuova Limited 7 // Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com) 8 // Portions Copyright © 2004,2006 Bruce Ellis 9 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 10 // Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others 11 // Portions Copyright © 2009 The Go Authors. All rights reserved. 12 // 13 // Permission is hereby granted, free of charge, to any person obtaining a copy 14 // of this software and associated documentation files (the "Software"), to deal 15 // in the Software without restriction, including without limitation the rights 16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 17 // copies of the Software, and to permit persons to whom the Software is 18 // furnished to do so, subject to the following conditions: 19 // 20 // The above copyright notice and this permission notice shall be included in 21 // all copies or substantial portions of the Software. 22 // 23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 29 // THE SOFTWARE. 30 31 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.Graph)(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 := (*gc.Flow)(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 := (*gc.Flow)(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, int(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 := (*gc.Flow)(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 := (*obj.Prog)(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 := (*gc.Flow)(b); ; r = r.Link { 342 fmt.Printf("\t%v\n", r.Prog) 343 if r == r0 { 344 break 345 } 346 } 347 } 348 349 t := obj.Prog(*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 := (*gc.Flow)(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 := (*obj.Prog)(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 := (*gc.Flow)(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 artihmetic 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 int) bool { 509 for r := (*gc.Flow)(gc.Uniqp(r0)); r != nil; r = gc.Uniqp(r) { 510 p := r.Prog 511 if p.To.Type == obj.TYPE_REG && int(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 522 return false 523 } 524 525 /* 526 * the idea is to substitute 527 * one register for another 528 * from one MOV to another 529 * MOV a, R0 530 * ADD b, R0 / no use of R1 531 * MOV R0, R1 532 * would be converted to 533 * MOV a, R1 534 * ADD b, R1 535 * MOV R1, R0 536 * hopefully, then the former or latter MOV 537 * will be eliminated by copy propagation. 538 */ 539 func subprop(r0 *gc.Flow) bool { 540 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 541 fmt.Printf("subprop %v\n", r0.Prog) 542 } 543 p := (*obj.Prog)(r0.Prog) 544 v1 := (*obj.Addr)(&p.From) 545 if !regtyp(v1) { 546 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 547 fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v1)) 548 } 549 return false 550 } 551 552 v2 := (*obj.Addr)(&p.To) 553 if !regtyp(v2) { 554 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 555 fmt.Printf("\tnot regtype %v; return 0\n", gc.Ctxt.Dconv(v2)) 556 } 557 return false 558 } 559 560 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 561 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 562 fmt.Printf("\t? %v\n", r.Prog) 563 } 564 if gc.Uniqs(r) == nil { 565 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 566 fmt.Printf("\tno unique successor\n") 567 } 568 break 569 } 570 571 p = r.Prog 572 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 573 continue 574 } 575 if p.Info.Flags&gc.Call != 0 { 576 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 577 fmt.Printf("\tfound %v; return 0\n", p) 578 } 579 return false 580 } 581 582 if p.Info.Reguse|p.Info.Regset != 0 { 583 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 584 fmt.Printf("\tfound %v; return 0\n", p) 585 } 586 return false 587 } 588 589 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 { 590 copysub(&p.To, v1, v2, 1) 591 if gc.Debug['P'] != 0 { 592 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 593 if p.From.Type == v2.Type && p.From.Reg == v2.Reg { 594 fmt.Printf(" excise") 595 } 596 fmt.Printf("\n") 597 } 598 599 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 600 p = r.Prog 601 copysub(&p.From, v1, v2, 1) 602 copysub(&p.To, v1, v2, 1) 603 if gc.Debug['P'] != 0 { 604 fmt.Printf("%v\n", r.Prog) 605 } 606 } 607 608 t := int(int(v1.Reg)) 609 v1.Reg = v2.Reg 610 v2.Reg = int16(t) 611 if gc.Debug['P'] != 0 { 612 fmt.Printf("%v last\n", r.Prog) 613 } 614 return true 615 } 616 617 if copyau(&p.From, v2) || copyau(&p.To, v2) { 618 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 619 fmt.Printf("\tcopyau %v failed\n", gc.Ctxt.Dconv(v2)) 620 } 621 break 622 } 623 624 if copysub(&p.From, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { 625 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 626 fmt.Printf("\tcopysub failed\n") 627 } 628 break 629 } 630 } 631 632 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 633 fmt.Printf("\tran off end; return 0\n") 634 } 635 return false 636 } 637 638 /* 639 * The idea is to remove redundant copies. 640 * v1->v2 F=0 641 * (use v2 s/v2/v1/)* 642 * set v1 F=1 643 * use v2 return fail 644 * ----------------- 645 * v1->v2 F=0 646 * (use v2 s/v2/v1/)* 647 * set v1 F=1 648 * set v2 return success 649 */ 650 func copyprop(g *gc.Graph, r0 *gc.Flow) bool { 651 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 652 fmt.Printf("copyprop %v\n", r0.Prog) 653 } 654 p := (*obj.Prog)(r0.Prog) 655 v1 := (*obj.Addr)(&p.From) 656 v2 := (*obj.Addr)(&p.To) 657 if copyas(v1, v2) { 658 return true 659 } 660 gactive++ 661 return copy1(v1, v2, r0.S1, 0) 662 } 663 664 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { 665 if uint32(r.Active) == gactive { 666 if gc.Debug['P'] != 0 { 667 fmt.Printf("act set; return 1\n") 668 } 669 return true 670 } 671 672 r.Active = int32(gactive) 673 if gc.Debug['P'] != 0 { 674 fmt.Printf("copy %v->%v f=%d\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), f) 675 } 676 var t int 677 var p *obj.Prog 678 for ; r != nil; r = r.S1 { 679 p = r.Prog 680 if gc.Debug['P'] != 0 { 681 fmt.Printf("%v", p) 682 } 683 if f == 0 && gc.Uniqp(r) == nil { 684 f = 1 685 if gc.Debug['P'] != 0 { 686 fmt.Printf("; merge; f=%d", f) 687 } 688 } 689 690 t = copyu(p, v2, nil) 691 switch t { 692 case 2: /* rar, can't split */ 693 if gc.Debug['P'] != 0 { 694 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 695 } 696 return false 697 698 case 3: /* set */ 699 if gc.Debug['P'] != 0 { 700 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 701 } 702 return true 703 704 case 1, /* used, substitute */ 705 4: /* use and set */ 706 if f != 0 { 707 if gc.Debug['P'] == 0 { 708 return false 709 } 710 if t == 4 { 711 fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 712 } else { 713 fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 714 } 715 return false 716 } 717 718 if copyu(p, v2, v1) != 0 { 719 if gc.Debug['P'] != 0 { 720 fmt.Printf("; sub fail; return 0\n") 721 } 722 return false 723 } 724 725 if gc.Debug['P'] != 0 { 726 fmt.Printf("; sub %v/%v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1)) 727 } 728 if t == 4 { 729 if gc.Debug['P'] != 0 { 730 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 731 } 732 return true 733 } 734 } 735 736 if f == 0 { 737 t = copyu(p, v1, nil) 738 if f == 0 && (t == 2 || t == 3 || t == 4) { 739 f = 1 740 if gc.Debug['P'] != 0 { 741 fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) 742 } 743 } 744 } 745 746 if gc.Debug['P'] != 0 { 747 fmt.Printf("\n") 748 } 749 if r.S2 != nil { 750 if !copy1(v1, v2, r.S2, f) { 751 return false 752 } 753 } 754 } 755 756 return true 757 } 758 759 /* 760 * return 761 * 1 if v only used (and substitute), 762 * 2 if read-alter-rewrite 763 * 3 if set 764 * 4 if set and used 765 * 0 otherwise (not touched) 766 */ 767 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 768 switch p.As { 769 case obj.AJMP: 770 if s != nil { 771 if copysub(&p.To, v, s, 1) != 0 { 772 return 1 773 } 774 return 0 775 } 776 777 if copyau(&p.To, v) { 778 return 1 779 } 780 return 0 781 782 case obj.ARET: 783 if s != nil { 784 return 1 785 } 786 return 3 787 788 case obj.ACALL: 789 if x86.REGEXT != 0 /*TypeKind(100016)*/ && v.Type == obj.TYPE_REG && v.Reg <= x86.REGEXT && v.Reg > exregoffset { 790 return 2 791 } 792 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 793 return 2 794 } 795 if v.Type == p.From.Type && v.Reg == p.From.Reg { 796 return 2 797 } 798 799 if s != nil { 800 if copysub(&p.To, v, s, 1) != 0 { 801 return 1 802 } 803 return 0 804 } 805 806 if copyau(&p.To, v) { 807 return 4 808 } 809 return 3 810 811 case obj.ATEXT: 812 if x86.REGARG >= 0 && v.Type == obj.TYPE_REG && v.Reg == x86.REGARG { 813 return 3 814 } 815 return 0 816 } 817 818 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 819 return 0 820 } 821 822 if (p.Info.Reguse|p.Info.Regset)&RtoB(int(v.Reg)) != 0 { 823 return 2 824 } 825 826 if (p.Info.Reguse|p.Info.Regset)&FtoB(int(v.Reg)) != 0 { 827 return 2 828 } 829 830 if p.Info.Flags&gc.LeftAddr != 0 { 831 if copyas(&p.From, v) { 832 return 2 833 } 834 } 835 836 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightRead|gc.RightWrite { 837 if copyas(&p.To, v) { 838 return 2 839 } 840 } 841 842 if p.Info.Flags&gc.RightWrite != 0 { 843 if copyas(&p.To, v) { 844 if s != nil { 845 return copysub(&p.From, v, s, 1) 846 } 847 if copyau(&p.From, v) { 848 return 4 849 } 850 return 3 851 } 852 } 853 854 if p.Info.Flags&(gc.LeftAddr|gc.LeftRead|gc.LeftWrite|gc.RightAddr|gc.RightRead|gc.RightWrite) != 0 { 855 if s != nil { 856 if copysub(&p.From, v, s, 1) != 0 { 857 return 1 858 } 859 return copysub(&p.To, v, s, 1) 860 } 861 862 if copyau(&p.From, v) { 863 return 1 864 } 865 if copyau(&p.To, v) { 866 return 1 867 } 868 } 869 870 return 0 871 } 872 873 /* 874 * direct reference, 875 * could be set/use depending on 876 * semantics 877 */ 878 func copyas(a *obj.Addr, v *obj.Addr) bool { 879 if x86.REG_AL <= a.Reg && a.Reg <= x86.REG_R15B { 880 gc.Fatalf("use of byte register") 881 } 882 if x86.REG_AL <= v.Reg && v.Reg <= x86.REG_R15B { 883 gc.Fatalf("use of byte register") 884 } 885 886 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 887 return false 888 } 889 if regtyp(v) { 890 return true 891 } 892 if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 893 if v.Offset == a.Offset { 894 return true 895 } 896 } 897 return false 898 } 899 900 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 901 if a.Type != v.Type || a.Name != v.Name || a.Reg != v.Reg { 902 return false 903 } 904 if regtyp(v) { 905 return true 906 } 907 if v.Type == obj.TYPE_MEM && (v.Name == obj.NAME_AUTO || v.Name == obj.NAME_PARAM) { 908 if v.Offset == a.Offset { 909 return true 910 } 911 } 912 return false 913 } 914 915 /* 916 * either direct or indirect 917 */ 918 func copyau(a *obj.Addr, v *obj.Addr) bool { 919 if copyas(a, v) { 920 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 921 fmt.Printf("\tcopyau: copyas returned 1\n") 922 } 923 return true 924 } 925 926 if regtyp(v) { 927 if a.Type == obj.TYPE_MEM && a.Reg == v.Reg { 928 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 929 fmt.Printf("\tcopyau: found indir use - return 1\n") 930 } 931 return true 932 } 933 934 if a.Index == v.Reg { 935 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 936 fmt.Printf("\tcopyau: found index use - return 1\n") 937 } 938 return true 939 } 940 } 941 942 return false 943 } 944 945 /* 946 * substitute s for v in a 947 * return failure to substitute 948 */ 949 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { 950 if copyas(a, v) { 951 reg := int(int(s.Reg)) 952 if reg >= x86.REG_AX && reg <= x86.REG_R15 || reg >= x86.REG_X0 && reg <= x86.REG_X0+15 { 953 if f != 0 { 954 a.Reg = int16(reg) 955 } 956 } 957 958 return 0 959 } 960 961 if regtyp(v) { 962 reg := int(int(v.Reg)) 963 if a.Type == obj.TYPE_MEM && int(a.Reg) == reg { 964 if (s.Reg == x86.REG_BP || s.Reg == x86.REG_R13) && a.Index != x86.REG_NONE { 965 return 1 /* can't use BP-base with index */ 966 } 967 if f != 0 { 968 a.Reg = s.Reg 969 } 970 } 971 972 // return 0; 973 if int(a.Index) == reg { 974 if f != 0 { 975 a.Index = s.Reg 976 } 977 return 0 978 } 979 980 return 0 981 } 982 983 return 0 984 } 985 986 func conprop(r0 *gc.Flow) { 987 var p *obj.Prog 988 var t int 989 990 p0 := (*obj.Prog)(r0.Prog) 991 v0 := (*obj.Addr)(&p0.To) 992 r := (*gc.Flow)(r0) 993 994 loop: 995 r = gc.Uniqs(r) 996 if r == nil || r == r0 { 997 return 998 } 999 if gc.Uniqp(r) == nil { 1000 return 1001 } 1002 1003 p = r.Prog 1004 t = copyu(p, v0, nil) 1005 switch t { 1006 case 0, // miss 1007 1: // use 1008 goto loop 1009 1010 case 2, // rar 1011 4: // use and set 1012 break 1013 1014 case 3: // set 1015 if p.As == p0.As { 1016 if p.From.Type == p0.From.Type { 1017 if p.From.Reg == p0.From.Reg { 1018 if p.From.Node == p0.From.Node { 1019 if p.From.Offset == p0.From.Offset { 1020 if p.From.Scale == p0.From.Scale { 1021 if p.From.Type == obj.TYPE_FCONST && p.From.Val.(float64) == p0.From.Val.(float64) { 1022 if p.From.Index == p0.From.Index { 1023 excise(r) 1024 goto loop 1025 } 1026 } 1027 } 1028 } 1029 } 1030 } 1031 } 1032 } 1033 } 1034 } 1035 1036 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 1037 return regtyp(reg) && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && a.Index == x86.REG_NONE && 0 <= a.Offset && a.Offset < 4096 1038 } 1039 1040 func stackaddr(a *obj.Addr) bool { 1041 return a.Type == obj.TYPE_REG && a.Reg == x86.REG_SP 1042 }