github.com/rsc/go@v0.0.0-20150416155037-e040fd465409/src/cmd/7g/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 main 32 33 import ( 34 "cmd/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/arm64" 37 "fmt" 38 ) 39 40 var gactive uint32 41 42 func peep(firstp *obj.Prog) { 43 g := (*gc.Graph)(gc.Flowstart(firstp, nil)) 44 if g == nil { 45 return 46 } 47 gactive = 0 48 49 var p *obj.Prog 50 var r *gc.Flow 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 61 // TODO(minux) Handle smaller moves. arm and amd64 62 // distinguish between moves that *must* sign/zero 63 // extend and moves that don't care so they 64 // can eliminate moves that don't care without 65 // breaking moves that do care. This might let us 66 // simplify or remove the next peep loop, too. 67 if p.As == arm64.AMOVD || p.As == arm64.AFMOVD { 68 if regtyp(&p.To) { 69 // Try to eliminate reg->reg moves 70 if regtyp(&p.From) { 71 if p.From.Type == p.To.Type { 72 if copyprop(r) { 73 excise(r) 74 t++ 75 } else if subprop(r) && copyprop(r) { 76 excise(r) 77 t++ 78 } 79 } 80 } 81 } 82 } 83 } 84 85 if t != 0 { 86 goto loop1 87 } 88 89 /* 90 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) 91 */ 92 var p1 *obj.Prog 93 var r1 *gc.Flow 94 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { 95 p = r.Prog 96 switch p.As { 97 default: 98 continue 99 100 case arm64.AMOVH, 101 arm64.AMOVHU, 102 arm64.AMOVB, 103 arm64.AMOVBU, 104 arm64.AMOVW, 105 arm64.AMOVWU: 106 if p.To.Type != obj.TYPE_REG { 107 continue 108 } 109 } 110 111 r1 = r.Link 112 if r1 == nil { 113 continue 114 } 115 p1 = r1.Prog 116 if p1.As != p.As { 117 continue 118 } 119 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { 120 continue 121 } 122 if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { 123 continue 124 } 125 excise(r1) 126 } 127 128 if gc.Debug['D'] > 1 { 129 goto ret /* allow following code improvement to be suppressed */ 130 } 131 132 // MOVD $c, R'; ADD R', R (R' unused) -> ADD $c, R 133 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { 134 p = r.Prog 135 switch p.As { 136 default: 137 continue 138 139 case arm64.AMOVD: 140 if p.To.Type != obj.TYPE_REG { 141 continue 142 } 143 if p.From.Type != obj.TYPE_CONST { 144 continue 145 } 146 if p.From.Offset < 0 || 4096 <= p.From.Offset { 147 continue 148 } 149 } 150 r1 = r.Link 151 if r1 == nil { 152 continue 153 } 154 p1 = r1.Prog 155 if p1.As != arm64.AADD && p1.As != arm64.ASUB { // TODO(aram): also logical after we have bimm. 156 continue 157 } 158 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { 159 continue 160 } 161 if p1.To.Type != obj.TYPE_REG { 162 continue 163 } 164 if gc.Debug['P'] != 0 { 165 fmt.Printf("encoding $%d directly into %v in:\n%v\n%v\n", p.From.Offset, obj.Aconv(int(p1.As)), p, p1) 166 } 167 p1.From.Type = obj.TYPE_CONST 168 p1.From = p.From 169 excise(r) 170 } 171 172 /* TODO(minux): 173 * look for OP x,y,R; CMP R, $0 -> OP.S x,y,R 174 * when OP can set condition codes correctly 175 */ 176 177 ret: 178 gc.Flowend(g) 179 } 180 181 func excise(r *gc.Flow) { 182 p := (*obj.Prog)(r.Prog) 183 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 184 fmt.Printf("%v ===delete===\n", p) 185 } 186 obj.Nopout(p) 187 gc.Ostats.Ndelmov++ 188 } 189 190 func regtyp(a *obj.Addr) bool { 191 // TODO(rsc): Floating point register exclusions? 192 return a.Type == obj.TYPE_REG && arm64.REG_R0 <= a.Reg && a.Reg <= arm64.REG_F31 && a.Reg != arm64.REGZERO 193 } 194 195 /* 196 * the idea is to substitute 197 * one register for another 198 * from one MOV to another 199 * MOV a, R1 200 * ADD b, R1 / no use of R2 201 * MOV R1, R2 202 * would be converted to 203 * MOV a, R2 204 * ADD b, R2 205 * MOV R2, R1 206 * hopefully, then the former or latter MOV 207 * will be eliminated by copy propagation. 208 * 209 * r0 (the argument, not the register) is the MOV at the end of the 210 * above sequences. This returns 1 if it modified any instructions. 211 */ 212 func subprop(r0 *gc.Flow) bool { 213 p := (*obj.Prog)(r0.Prog) 214 v1 := (*obj.Addr)(&p.From) 215 if !regtyp(v1) { 216 return false 217 } 218 v2 := (*obj.Addr)(&p.To) 219 if !regtyp(v2) { 220 return false 221 } 222 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 223 if gc.Uniqs(r) == nil { 224 break 225 } 226 p = r.Prog 227 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 228 continue 229 } 230 if p.Info.Flags&gc.Call != 0 { 231 return false 232 } 233 234 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { 235 if p.To.Type == v1.Type { 236 if p.To.Reg == v1.Reg { 237 copysub(&p.To, v1, v2, 1) 238 if gc.Debug['P'] != 0 { 239 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 240 if p.From.Type == v2.Type { 241 fmt.Printf(" excise") 242 } 243 fmt.Printf("\n") 244 } 245 246 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 247 p = r.Prog 248 copysub(&p.From, v1, v2, 1) 249 copysub1(p, v1, v2, 1) 250 copysub(&p.To, v1, v2, 1) 251 if gc.Debug['P'] != 0 { 252 fmt.Printf("%v\n", r.Prog) 253 } 254 } 255 256 t := int(int(v1.Reg)) 257 v1.Reg = v2.Reg 258 v2.Reg = int16(t) 259 if gc.Debug['P'] != 0 { 260 fmt.Printf("%v last\n", r.Prog) 261 } 262 return true 263 } 264 } 265 } 266 267 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { 268 break 269 } 270 if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { 271 break 272 } 273 } 274 275 return false 276 } 277 278 /* 279 * The idea is to remove redundant copies. 280 * v1->v2 F=0 281 * (use v2 s/v2/v1/)* 282 * set v1 F=1 283 * use v2 return fail (v1->v2 move must remain) 284 * ----------------- 285 * v1->v2 F=0 286 * (use v2 s/v2/v1/)* 287 * set v1 F=1 288 * set v2 return success (caller can remove v1->v2 move) 289 */ 290 func copyprop(r0 *gc.Flow) bool { 291 p := (*obj.Prog)(r0.Prog) 292 v1 := (*obj.Addr)(&p.From) 293 v2 := (*obj.Addr)(&p.To) 294 if copyas(v1, v2) { 295 if gc.Debug['P'] != 0 { 296 fmt.Printf("eliminating self-move\n", r0.Prog) 297 } 298 return true 299 } 300 301 gactive++ 302 if gc.Debug['P'] != 0 { 303 fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) 304 } 305 return copy1(v1, v2, r0.S1, 0) 306 } 307 308 // copy1 replaces uses of v2 with v1 starting at r and returns 1 if 309 // all uses were rewritten. 310 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { 311 if uint32(r.Active) == gactive { 312 if gc.Debug['P'] != 0 { 313 fmt.Printf("act set; return 1\n") 314 } 315 return true 316 } 317 318 r.Active = int32(gactive) 319 if gc.Debug['P'] != 0 { 320 fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) 321 } 322 var t int 323 var p *obj.Prog 324 for ; r != nil; r = r.S1 { 325 p = r.Prog 326 if gc.Debug['P'] != 0 { 327 fmt.Printf("%v", p) 328 } 329 if f == 0 && gc.Uniqp(r) == nil { 330 // Multiple predecessors; conservatively 331 // assume v1 was set on other path 332 f = 1 333 334 if gc.Debug['P'] != 0 { 335 fmt.Printf("; merge; f=%d", f) 336 } 337 } 338 339 t = copyu(p, v2, nil) 340 switch t { 341 case 2: /* rar, can't split */ 342 if gc.Debug['P'] != 0 { 343 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 344 } 345 return false 346 347 case 3: /* set */ 348 if gc.Debug['P'] != 0 { 349 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 350 } 351 return true 352 353 case 1, /* used, substitute */ 354 4: /* use and set */ 355 if f != 0 { 356 if gc.Debug['P'] == 0 { 357 return false 358 } 359 if t == 4 { 360 fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 361 } else { 362 fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 363 } 364 return false 365 } 366 367 if copyu(p, v2, v1) != 0 { 368 if gc.Debug['P'] != 0 { 369 fmt.Printf("; sub fail; return 0\n") 370 } 371 return false 372 } 373 374 if gc.Debug['P'] != 0 { 375 fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) 376 } 377 if t == 4 { 378 if gc.Debug['P'] != 0 { 379 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 380 } 381 return true 382 } 383 } 384 385 if f == 0 { 386 t = copyu(p, v1, nil) 387 if f == 0 && (t == 2 || t == 3 || t == 4) { 388 f = 1 389 if gc.Debug['P'] != 0 { 390 fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) 391 } 392 } 393 } 394 395 if gc.Debug['P'] != 0 { 396 fmt.Printf("\n") 397 } 398 if r.S2 != nil { 399 if !copy1(v1, v2, r.S2, f) { 400 return false 401 } 402 } 403 } 404 405 return true 406 } 407 408 // If s==nil, copyu returns the set/use of v in p; otherwise, it 409 // modifies p to replace reads of v with reads of s and returns 0 for 410 // success or non-zero for failure. 411 // 412 // If s==nil, copy returns one of the following values: 413 // 1 if v only used 414 // 2 if v is set and used in one address (read-alter-rewrite; 415 // can't substitute) 416 // 3 if v is only set 417 // 4 if v is set in one address and used in another (so addresses 418 // can be rewritten independently) 419 // 0 otherwise (not touched) 420 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 421 if p.From3.Type != obj.TYPE_NONE { 422 // 7g never generates a from3 423 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(&p.From3)) 424 } 425 if p.To2.Type != obj.TYPE_NONE { 426 // 7g never generates a to2 427 fmt.Printf("copyu: to2 (%v) not implemented\n", gc.Ctxt.Dconv(&p.To2)) 428 } 429 430 switch p.As { 431 default: 432 fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) 433 return 2 434 435 case obj.ANOP, /* read p->from, write p->to */ 436 arm64.ANEG, 437 arm64.AFNEGD, 438 arm64.AFNEGS, 439 arm64.AFSQRTD, 440 arm64.AFCVTZSD, 441 arm64.AFCVTZSS, 442 arm64.AFCVTZSDW, 443 arm64.AFCVTZSSW, 444 arm64.AFCVTZUD, 445 arm64.AFCVTZUS, 446 arm64.AFCVTZUDW, 447 arm64.AFCVTZUSW, 448 arm64.AFCVTSD, 449 arm64.AFCVTDS, 450 arm64.ASCVTFD, 451 arm64.ASCVTFS, 452 arm64.ASCVTFWD, 453 arm64.ASCVTFWS, 454 arm64.AUCVTFD, 455 arm64.AUCVTFS, 456 arm64.AUCVTFWD, 457 arm64.AUCVTFWS, 458 arm64.AMOVB, 459 arm64.AMOVBU, 460 arm64.AMOVH, 461 arm64.AMOVHU, 462 arm64.AMOVW, 463 arm64.AMOVWU, 464 arm64.AMOVD, 465 arm64.AFMOVS, 466 arm64.AFMOVD: 467 if p.Scond == 0 { 468 if s != nil { 469 if copysub(&p.From, v, s, 1) != 0 { 470 return 1 471 } 472 473 // Update only indirect uses of v in p->to 474 if !copyas(&p.To, v) { 475 if copysub(&p.To, v, s, 1) != 0 { 476 return 1 477 } 478 } 479 return 0 480 } 481 482 if copyas(&p.To, v) { 483 // Fix up implicit from 484 if p.From.Type == obj.TYPE_NONE { 485 p.From = p.To 486 } 487 if copyau(&p.From, v) { 488 return 4 489 } 490 return 3 491 } 492 493 if copyau(&p.From, v) { 494 return 1 495 } 496 if copyau(&p.To, v) { 497 // p->to only indirectly uses v 498 return 1 499 } 500 501 return 0 502 } 503 504 /* rar p->from, write p->to or read p->from, rar p->to */ 505 if p.From.Type == obj.TYPE_MEM { 506 if copyas(&p.From, v) { 507 // No s!=nil check; need to fail 508 // anyway in that case 509 return 2 510 } 511 512 if s != nil { 513 if copysub(&p.To, v, s, 1) != 0 { 514 return 1 515 } 516 return 0 517 } 518 519 if copyas(&p.To, v) { 520 return 3 521 } 522 } else if p.To.Type == obj.TYPE_MEM { 523 if copyas(&p.To, v) { 524 return 2 525 } 526 if s != nil { 527 if copysub(&p.From, v, s, 1) != 0 { 528 return 1 529 } 530 return 0 531 } 532 533 if copyau(&p.From, v) { 534 return 1 535 } 536 } else { 537 fmt.Printf("copyu: bad %v\n", p) 538 } 539 540 return 0 541 542 case arm64.AADD, /* read p->from, read p->reg, write p->to */ 543 arm64.ASUB, 544 arm64.AAND, 545 arm64.AORR, 546 arm64.AEOR, 547 arm64.AMUL, 548 arm64.ASMULL, 549 arm64.AUMULL, 550 arm64.ASMULH, 551 arm64.AUMULH, 552 arm64.ASDIV, 553 arm64.AUDIV, 554 arm64.ALSL, 555 arm64.ALSR, 556 arm64.AASR, 557 arm64.AFADDD, 558 arm64.AFADDS, 559 arm64.AFSUBD, 560 arm64.AFSUBS, 561 arm64.AFMULD, 562 arm64.AFMULS, 563 arm64.AFDIVD, 564 arm64.AFDIVS: 565 if s != nil { 566 if copysub(&p.From, v, s, 1) != 0 { 567 return 1 568 } 569 if copysub1(p, v, s, 1) != 0 { 570 return 1 571 } 572 573 // Update only indirect uses of v in p->to 574 if !copyas(&p.To, v) { 575 if copysub(&p.To, v, s, 1) != 0 { 576 return 1 577 } 578 } 579 return 0 580 } 581 582 if copyas(&p.To, v) { 583 if p.Reg == 0 { 584 // Fix up implicit reg (e.g., ADD 585 // R3,R4 -> ADD R3,R4,R4) so we can 586 // update reg and to separately. 587 p.Reg = p.To.Reg 588 } 589 590 if copyau(&p.From, v) { 591 return 4 592 } 593 if copyau1(p, v) { 594 return 4 595 } 596 return 3 597 } 598 599 if copyau(&p.From, v) { 600 return 1 601 } 602 if copyau1(p, v) { 603 return 1 604 } 605 if copyau(&p.To, v) { 606 return 1 607 } 608 return 0 609 610 case arm64.ABEQ, 611 arm64.ABNE, 612 arm64.ABGE, 613 arm64.ABLT, 614 arm64.ABGT, 615 arm64.ABLE, 616 arm64.ABLO, 617 arm64.ABLS, 618 arm64.ABHI, 619 arm64.ABHS: 620 return 0 621 622 case obj.ACHECKNIL, /* read p->from */ 623 arm64.ACMP, /* read p->from, read p->reg */ 624 arm64.AFCMPD, 625 arm64.AFCMPS: 626 if s != nil { 627 if copysub(&p.From, v, s, 1) != 0 { 628 return 1 629 } 630 return copysub1(p, v, s, 1) 631 } 632 633 if copyau(&p.From, v) { 634 return 1 635 } 636 if copyau1(p, v) { 637 return 1 638 } 639 return 0 640 641 case arm64.AB: /* read p->to */ 642 if s != nil { 643 if copysub(&p.To, v, s, 1) != 0 { 644 return 1 645 } 646 return 0 647 } 648 649 if copyau(&p.To, v) { 650 return 1 651 } 652 return 0 653 654 case obj.ARET: /* funny */ 655 if s != nil { 656 return 0 657 } 658 659 // All registers die at this point, so claim 660 // everything is set (and not used). 661 return 3 662 663 case arm64.ABL: /* funny */ 664 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { 665 return 2 666 } 667 668 if s != nil { 669 if copysub(&p.To, v, s, 1) != 0 { 670 return 1 671 } 672 return 0 673 } 674 675 if copyau(&p.To, v) { 676 return 4 677 } 678 return 3 679 680 // R31 is zero, used by DUFFZERO, cannot be substituted. 681 // R16 is ptr to memory, used and set, cannot be substituted. 682 case obj.ADUFFZERO: 683 if v.Type == obj.TYPE_REG { 684 if v.Reg == 31 { 685 return 1 686 } 687 if v.Reg == 16 { 688 return 2 689 } 690 } 691 692 return 0 693 694 // R16, R17 are ptr to src, dst, used and set, cannot be substituted. 695 // R27 is scratch, set by DUFFCOPY, cannot be substituted. 696 case obj.ADUFFCOPY: 697 if v.Type == obj.TYPE_REG { 698 if v.Reg == 16 || v.Reg == 17 { 699 return 2 700 } 701 if v.Reg == 27 { 702 return 3 703 } 704 } 705 706 return 0 707 708 case arm64.AHINT, 709 obj.ATEXT, 710 obj.APCDATA, 711 obj.AFUNCDATA, 712 obj.AVARDEF, 713 obj.AVARKILL: 714 return 0 715 } 716 } 717 718 // copyas returns 1 if a and v address the same register. 719 // 720 // If a is the from operand, this means this operation reads the 721 // register in v. If a is the to operand, this means this operation 722 // writes the register in v. 723 func copyas(a *obj.Addr, v *obj.Addr) bool { 724 if regtyp(v) { 725 if a.Type == v.Type { 726 if a.Reg == v.Reg { 727 return true 728 } 729 } 730 } 731 return false 732 } 733 734 // copyau returns 1 if a either directly or indirectly addresses the 735 // same register as v. 736 // 737 // If a is the from operand, this means this operation reads the 738 // register in v. If a is the to operand, this means the operation 739 // either reads or writes the register in v (if !copyas(a, v), then 740 // the operation reads the register in v). 741 func copyau(a *obj.Addr, v *obj.Addr) bool { 742 if copyas(a, v) { 743 return true 744 } 745 if v.Type == obj.TYPE_REG { 746 if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { 747 if v.Reg == a.Reg { 748 return true 749 } 750 } 751 } 752 return false 753 } 754 755 // copyau1 returns 1 if p->reg references the same register as v and v 756 // is a direct reference. 757 func copyau1(p *obj.Prog, v *obj.Addr) bool { 758 if regtyp(v) && v.Reg != 0 { 759 if p.Reg == v.Reg { 760 return true 761 } 762 } 763 return false 764 } 765 766 // copysub replaces v with s in a if f!=0 or indicates it if could if f==0. 767 // Returns 1 on failure to substitute (it always succeeds on arm64). 768 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { 769 if f != 0 { 770 if copyau(a, v) { 771 a.Reg = s.Reg 772 } 773 } 774 return 0 775 } 776 777 // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. 778 // Returns 1 on failure to substitute (it always succeeds on arm64). 779 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { 780 if f != 0 { 781 if copyau1(p1, v) { 782 p1.Reg = s.Reg 783 } 784 } 785 return 0 786 } 787 788 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 789 if a.Type != v.Type { 790 return false 791 } 792 if regtyp(v) && a.Reg == v.Reg { 793 return true 794 } 795 if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { 796 if v.Offset == a.Offset { 797 return true 798 } 799 } 800 return false 801 } 802 803 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 804 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 805 } 806 807 func stackaddr(a *obj.Addr) bool { 808 return a.Type == obj.TYPE_REG && a.Reg == arm64.REGSP 809 }