github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/cmd/compile/internal/mips64/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 mips64 32 33 import ( 34 "cmd/compile/internal/gc" 35 "cmd/internal/obj" 36 "cmd/internal/obj/mips" 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(austin) Handle smaller moves. arm and amd64 62 // distinguish between moves that moves that *must* 63 // sign/zero 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 == mips.AMOVV || p.As == mips.AMOVF || p.As == mips.AMOVD { 68 if regtyp(&p.To) { 69 // Try to eliminate reg->reg moves 70 if regtyp(&p.From) { 71 if isfreg(&p.From) == isfreg(&p.To) { 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 // Convert uses to $0 to uses of R0 and 83 // propagate R0 84 if regzer(&p.From) != 0 { 85 if p.To.Type == obj.TYPE_REG && !isfreg(&p.To) { 86 p.From.Type = obj.TYPE_REG 87 p.From.Reg = mips.REGZERO 88 if copyprop(r) { 89 excise(r) 90 t++ 91 } else if subprop(r) && copyprop(r) { 92 excise(r) 93 t++ 94 } 95 } 96 } 97 } 98 } 99 } 100 101 if t != 0 { 102 goto loop1 103 } 104 105 /* 106 * look for MOVB x,R; MOVB R,R (for small MOVs not handled above) 107 */ 108 var p1 *obj.Prog 109 var r1 *gc.Flow 110 for r := (*gc.Flow)(g.Start); r != nil; r = r.Link { 111 p = r.Prog 112 switch p.As { 113 default: 114 continue 115 116 case mips.AMOVH, 117 mips.AMOVHU, 118 mips.AMOVB, 119 mips.AMOVBU, 120 mips.AMOVW, 121 mips.AMOVWU: 122 if p.To.Type != obj.TYPE_REG { 123 continue 124 } 125 } 126 127 r1 = r.Link 128 if r1 == nil { 129 continue 130 } 131 p1 = r1.Prog 132 if p1.As != p.As { 133 continue 134 } 135 if p1.From.Type != obj.TYPE_REG || p1.From.Reg != p.To.Reg { 136 continue 137 } 138 if p1.To.Type != obj.TYPE_REG || p1.To.Reg != p.To.Reg { 139 continue 140 } 141 excise(r1) 142 } 143 144 gc.Flowend(g) 145 } 146 147 func excise(r *gc.Flow) { 148 p := (*obj.Prog)(r.Prog) 149 if gc.Debug['P'] != 0 && gc.Debug['v'] != 0 { 150 fmt.Printf("%v ===delete===\n", p) 151 } 152 obj.Nopout(p) 153 gc.Ostats.Ndelmov++ 154 } 155 156 /* 157 * regzer returns 1 if a's value is 0 (a is R0 or $0) 158 */ 159 func regzer(a *obj.Addr) int { 160 if a.Type == obj.TYPE_CONST || a.Type == obj.TYPE_ADDR { 161 if a.Sym == nil && a.Reg == 0 { 162 if a.Offset == 0 { 163 return 1 164 } 165 } 166 } 167 if a.Type == obj.TYPE_REG { 168 if a.Reg == mips.REGZERO { 169 return 1 170 } 171 } 172 return 0 173 } 174 175 func regtyp(a *obj.Addr) bool { 176 // TODO(rsc): Floating point register exclusions? 177 return a.Type == obj.TYPE_REG && mips.REG_R0 <= a.Reg && a.Reg <= mips.REG_F31 && a.Reg != mips.REGZERO 178 } 179 180 func isfreg(a *obj.Addr) bool { 181 return mips.REG_F0 <= a.Reg && a.Reg <= mips.REG_F31 182 } 183 184 /* 185 * the idea is to substitute 186 * one register for another 187 * from one MOV to another 188 * MOV a, R1 189 * ADD b, R1 / no use of R2 190 * MOV R1, R2 191 * would be converted to 192 * MOV a, R2 193 * ADD b, R2 194 * MOV R2, R1 195 * hopefully, then the former or latter MOV 196 * will be eliminated by copy propagation. 197 * 198 * r0 (the argument, not the register) is the MOV at the end of the 199 * above sequences. This returns 1 if it modified any instructions. 200 */ 201 func subprop(r0 *gc.Flow) bool { 202 p := (*obj.Prog)(r0.Prog) 203 v1 := (*obj.Addr)(&p.From) 204 if !regtyp(v1) { 205 return false 206 } 207 v2 := (*obj.Addr)(&p.To) 208 if !regtyp(v2) { 209 return false 210 } 211 for r := gc.Uniqp(r0); r != nil; r = gc.Uniqp(r) { 212 if gc.Uniqs(r) == nil { 213 break 214 } 215 p = r.Prog 216 if p.As == obj.AVARDEF || p.As == obj.AVARKILL { 217 continue 218 } 219 if p.Info.Flags&gc.Call != 0 { 220 return false 221 } 222 223 if p.Info.Flags&(gc.RightRead|gc.RightWrite) == gc.RightWrite { 224 if p.To.Type == v1.Type { 225 if p.To.Reg == v1.Reg { 226 copysub(&p.To, v1, v2, 1) 227 if gc.Debug['P'] != 0 { 228 fmt.Printf("gotit: %v->%v\n%v", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r.Prog) 229 if p.From.Type == v2.Type { 230 fmt.Printf(" excise") 231 } 232 fmt.Printf("\n") 233 } 234 235 for r = gc.Uniqs(r); r != r0; r = gc.Uniqs(r) { 236 p = r.Prog 237 copysub(&p.From, v1, v2, 1) 238 copysub1(p, v1, v2, 1) 239 copysub(&p.To, v1, v2, 1) 240 if gc.Debug['P'] != 0 { 241 fmt.Printf("%v\n", r.Prog) 242 } 243 } 244 245 t := int(int(v1.Reg)) 246 v1.Reg = v2.Reg 247 v2.Reg = int16(t) 248 if gc.Debug['P'] != 0 { 249 fmt.Printf("%v last\n", r.Prog) 250 } 251 return true 252 } 253 } 254 } 255 256 if copyau(&p.From, v2) || copyau1(p, v2) || copyau(&p.To, v2) { 257 break 258 } 259 if copysub(&p.From, v1, v2, 0) != 0 || copysub1(p, v1, v2, 0) != 0 || copysub(&p.To, v1, v2, 0) != 0 { 260 break 261 } 262 } 263 264 return false 265 } 266 267 /* 268 * The idea is to remove redundant copies. 269 * v1->v2 F=0 270 * (use v2 s/v2/v1/)* 271 * set v1 F=1 272 * use v2 return fail (v1->v2 move must remain) 273 * ----------------- 274 * v1->v2 F=0 275 * (use v2 s/v2/v1/)* 276 * set v1 F=1 277 * set v2 return success (caller can remove v1->v2 move) 278 */ 279 func copyprop(r0 *gc.Flow) bool { 280 p := (*obj.Prog)(r0.Prog) 281 v1 := (*obj.Addr)(&p.From) 282 v2 := (*obj.Addr)(&p.To) 283 if copyas(v1, v2) { 284 if gc.Debug['P'] != 0 { 285 fmt.Printf("eliminating self-move: %v\n", r0.Prog) 286 } 287 return true 288 } 289 290 gactive++ 291 if gc.Debug['P'] != 0 { 292 fmt.Printf("trying to eliminate %v->%v move from:\n%v\n", gc.Ctxt.Dconv(v1), gc.Ctxt.Dconv(v2), r0.Prog) 293 } 294 return copy1(v1, v2, r0.S1, 0) 295 } 296 297 // copy1 replaces uses of v2 with v1 starting at r and returns 1 if 298 // all uses were rewritten. 299 func copy1(v1 *obj.Addr, v2 *obj.Addr, r *gc.Flow, f int) bool { 300 if uint32(r.Active) == gactive { 301 if gc.Debug['P'] != 0 { 302 fmt.Printf("act set; return 1\n") 303 } 304 return true 305 } 306 307 r.Active = int32(gactive) 308 if gc.Debug['P'] != 0 { 309 fmt.Printf("copy1 replace %v with %v f=%d\n", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), f) 310 } 311 var t int 312 var p *obj.Prog 313 for ; r != nil; r = r.S1 { 314 p = r.Prog 315 if gc.Debug['P'] != 0 { 316 fmt.Printf("%v", p) 317 } 318 if f == 0 && gc.Uniqp(r) == nil { 319 // Multiple predecessors; conservatively 320 // assume v1 was set on other path 321 f = 1 322 323 if gc.Debug['P'] != 0 { 324 fmt.Printf("; merge; f=%d", f) 325 } 326 } 327 328 t = copyu(p, v2, nil) 329 switch t { 330 case 2: /* rar, can't split */ 331 if gc.Debug['P'] != 0 { 332 fmt.Printf("; %v rar; return 0\n", gc.Ctxt.Dconv(v2)) 333 } 334 return false 335 336 case 3: /* set */ 337 if gc.Debug['P'] != 0 { 338 fmt.Printf("; %v set; return 1\n", gc.Ctxt.Dconv(v2)) 339 } 340 return true 341 342 case 1, /* used, substitute */ 343 4: /* use and set */ 344 if f != 0 { 345 if gc.Debug['P'] == 0 { 346 return false 347 } 348 if t == 4 { 349 fmt.Printf("; %v used+set and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 350 } else { 351 fmt.Printf("; %v used and f=%d; return 0\n", gc.Ctxt.Dconv(v2), f) 352 } 353 return false 354 } 355 356 if copyu(p, v2, v1) != 0 { 357 if gc.Debug['P'] != 0 { 358 fmt.Printf("; sub fail; return 0\n") 359 } 360 return false 361 } 362 363 if gc.Debug['P'] != 0 { 364 fmt.Printf("; sub %v->%v\n => %v", gc.Ctxt.Dconv(v2), gc.Ctxt.Dconv(v1), p) 365 } 366 if t == 4 { 367 if gc.Debug['P'] != 0 { 368 fmt.Printf("; %v used+set; return 1\n", gc.Ctxt.Dconv(v2)) 369 } 370 return true 371 } 372 } 373 374 if f == 0 { 375 t = copyu(p, v1, nil) 376 if f == 0 && (t == 2 || t == 3 || t == 4) { 377 f = 1 378 if gc.Debug['P'] != 0 { 379 fmt.Printf("; %v set and !f; f=%d", gc.Ctxt.Dconv(v1), f) 380 } 381 } 382 } 383 384 if gc.Debug['P'] != 0 { 385 fmt.Printf("\n") 386 } 387 if r.S2 != nil { 388 if !copy1(v1, v2, r.S2, f) { 389 return false 390 } 391 } 392 } 393 394 return true 395 } 396 397 // If s==nil, copyu returns the set/use of v in p; otherwise, it 398 // modifies p to replace reads of v with reads of s and returns 0 for 399 // success or non-zero for failure. 400 // 401 // If s==nil, copy returns one of the following values: 402 // 1 if v only used 403 // 2 if v is set and used in one address (read-alter-rewrite; 404 // can't substitute) 405 // 3 if v is only set 406 // 4 if v is set in one address and used in another (so addresses 407 // can be rewritten independently) 408 // 0 otherwise (not touched) 409 func copyu(p *obj.Prog, v *obj.Addr, s *obj.Addr) int { 410 if p.From3Type() != obj.TYPE_NONE { 411 // never generates a from3 412 fmt.Printf("copyu: from3 (%v) not implemented\n", gc.Ctxt.Dconv(p.From3)) 413 } 414 415 switch p.As { 416 default: 417 fmt.Printf("copyu: can't find %v\n", obj.Aconv(int(p.As))) 418 return 2 419 420 case obj.ANOP, /* read p->from, write p->to */ 421 mips.AMOVV, 422 mips.AMOVF, 423 mips.AMOVD, 424 mips.AMOVH, 425 mips.AMOVHU, 426 mips.AMOVB, 427 mips.AMOVBU, 428 mips.AMOVW, 429 mips.AMOVWU, 430 mips.AMOVFD, 431 mips.AMOVDF, 432 mips.AMOVDW, 433 mips.AMOVWD, 434 mips.AMOVFW, 435 mips.AMOVWF, 436 mips.AMOVDV, 437 mips.AMOVVD, 438 mips.AMOVFV, 439 mips.AMOVVF, 440 mips.ATRUNCFV, 441 mips.ATRUNCDV, 442 mips.ATRUNCFW, 443 mips.ATRUNCDW: 444 if s != nil { 445 if copysub(&p.From, v, s, 1) != 0 { 446 return 1 447 } 448 449 // Update only indirect uses of v in p->to 450 if !copyas(&p.To, v) { 451 if copysub(&p.To, v, s, 1) != 0 { 452 return 1 453 } 454 } 455 return 0 456 } 457 458 if copyas(&p.To, v) { 459 // Fix up implicit from 460 if p.From.Type == obj.TYPE_NONE { 461 p.From = p.To 462 } 463 if copyau(&p.From, v) { 464 return 4 465 } 466 return 3 467 } 468 469 if copyau(&p.From, v) { 470 return 1 471 } 472 if copyau(&p.To, v) { 473 // p->to only indirectly uses v 474 return 1 475 } 476 477 return 0 478 479 case mips.ASGT, /* read p->from, read p->reg, write p->to */ 480 mips.ASGTU, 481 482 mips.AADD, 483 mips.AADDU, 484 mips.ASUB, 485 mips.ASUBU, 486 mips.ASLL, 487 mips.ASRL, 488 mips.ASRA, 489 mips.AOR, 490 mips.ANOR, 491 mips.AAND, 492 mips.AXOR, 493 494 mips.AADDV, 495 mips.AADDVU, 496 mips.ASUBV, 497 mips.ASUBVU, 498 mips.ASLLV, 499 mips.ASRLV, 500 mips.ASRAV, 501 502 mips.AADDF, 503 mips.AADDD, 504 mips.ASUBF, 505 mips.ASUBD, 506 mips.AMULF, 507 mips.AMULD, 508 mips.ADIVF, 509 mips.ADIVD: 510 if s != nil { 511 if copysub(&p.From, v, s, 1) != 0 { 512 return 1 513 } 514 if copysub1(p, v, s, 1) != 0 { 515 return 1 516 } 517 518 // Update only indirect uses of v in p->to 519 if !copyas(&p.To, v) { 520 if copysub(&p.To, v, s, 1) != 0 { 521 return 1 522 } 523 } 524 return 0 525 } 526 527 if copyas(&p.To, v) { 528 if p.Reg == 0 { 529 // Fix up implicit reg (e.g., ADD 530 // R3,R4 -> ADD R3,R4,R4) so we can 531 // update reg and to separately. 532 p.Reg = p.To.Reg 533 } 534 535 if copyau(&p.From, v) { 536 return 4 537 } 538 if copyau1(p, v) { 539 return 4 540 } 541 return 3 542 } 543 544 if copyau(&p.From, v) { 545 return 1 546 } 547 if copyau1(p, v) { 548 return 1 549 } 550 if copyau(&p.To, v) { 551 return 1 552 } 553 return 0 554 555 case obj.ACHECKNIL, /* read p->from */ 556 mips.ABEQ, /* read p->from, read p->reg */ 557 mips.ABNE, 558 mips.ABGTZ, 559 mips.ABGEZ, 560 mips.ABLTZ, 561 mips.ABLEZ, 562 563 mips.ACMPEQD, 564 mips.ACMPEQF, 565 mips.ACMPGED, 566 mips.ACMPGEF, 567 mips.ACMPGTD, 568 mips.ACMPGTF, 569 mips.ABFPF, 570 mips.ABFPT, 571 572 mips.AMUL, 573 mips.AMULU, 574 mips.ADIV, 575 mips.ADIVU, 576 mips.AMULV, 577 mips.AMULVU, 578 mips.ADIVV, 579 mips.ADIVVU: 580 if s != nil { 581 if copysub(&p.From, v, s, 1) != 0 { 582 return 1 583 } 584 return copysub1(p, v, s, 1) 585 } 586 587 if copyau(&p.From, v) { 588 return 1 589 } 590 if copyau1(p, v) { 591 return 1 592 } 593 return 0 594 595 case mips.AJMP: /* read p->to */ 596 if s != nil { 597 if copysub(&p.To, v, s, 1) != 0 { 598 return 1 599 } 600 return 0 601 } 602 603 if copyau(&p.To, v) { 604 return 1 605 } 606 return 0 607 608 case mips.ARET: /* funny */ 609 if s != nil { 610 return 0 611 } 612 613 // All registers die at this point, so claim 614 // everything is set (and not used). 615 return 3 616 617 case mips.AJAL: /* funny */ 618 if v.Type == obj.TYPE_REG { 619 // TODO(rsc): REG_R0 and REG_F0 used to be 620 // (when register numbers started at 0) exregoffset and exfregoffset, 621 // which are unset entirely. 622 // It's strange that this handles R0 and F0 differently from the other 623 // registers. Possible failure to optimize? 624 if mips.REG_R0 < v.Reg && v.Reg <= mips.REG_R31 { 625 return 2 626 } 627 if v.Reg == mips.REGARG { 628 return 2 629 } 630 if mips.REG_F0 < v.Reg && v.Reg <= mips.REG_F31 { 631 return 2 632 } 633 } 634 635 if p.From.Type == obj.TYPE_REG && v.Type == obj.TYPE_REG && p.From.Reg == v.Reg { 636 return 2 637 } 638 639 if s != nil { 640 if copysub(&p.To, v, s, 1) != 0 { 641 return 1 642 } 643 return 0 644 } 645 646 if copyau(&p.To, v) { 647 return 4 648 } 649 return 3 650 651 // R0 is zero, used by DUFFZERO, cannot be substituted. 652 // R1 is ptr to memory, used and set, cannot be substituted. 653 case obj.ADUFFZERO: 654 if v.Type == obj.TYPE_REG { 655 if v.Reg == 0 { 656 return 1 657 } 658 if v.Reg == 1 { 659 return 2 660 } 661 } 662 663 return 0 664 665 // R1, R2 are ptr to src, dst, used and set, cannot be substituted. 666 // R3 is scratch, set by DUFFCOPY, cannot be substituted. 667 case obj.ADUFFCOPY: 668 if v.Type == obj.TYPE_REG { 669 if v.Reg == 1 || v.Reg == 2 { 670 return 2 671 } 672 if v.Reg == 3 { 673 return 3 674 } 675 } 676 677 return 0 678 679 case obj.ATEXT: /* funny */ 680 if v.Type == obj.TYPE_REG { 681 if v.Reg == mips.REGARG { 682 return 3 683 } 684 } 685 return 0 686 687 case obj.APCDATA, 688 obj.AFUNCDATA, 689 obj.AVARDEF, 690 obj.AVARKILL: 691 return 0 692 } 693 } 694 695 // copyas returns 1 if a and v address the same register. 696 // 697 // If a is the from operand, this means this operation reads the 698 // register in v. If a is the to operand, this means this operation 699 // writes the register in v. 700 func copyas(a *obj.Addr, v *obj.Addr) bool { 701 if regtyp(v) { 702 if a.Type == v.Type { 703 if a.Reg == v.Reg { 704 return true 705 } 706 } 707 } 708 return false 709 } 710 711 // copyau returns 1 if a either directly or indirectly addresses the 712 // same register as v. 713 // 714 // If a is the from operand, this means this operation reads the 715 // register in v. If a is the to operand, this means the operation 716 // either reads or writes the register in v (if !copyas(a, v), then 717 // the operation reads the register in v). 718 func copyau(a *obj.Addr, v *obj.Addr) bool { 719 if copyas(a, v) { 720 return true 721 } 722 if v.Type == obj.TYPE_REG { 723 if a.Type == obj.TYPE_MEM || (a.Type == obj.TYPE_ADDR && a.Reg != 0) { 724 if v.Reg == a.Reg { 725 return true 726 } 727 } 728 } 729 return false 730 } 731 732 // copyau1 returns 1 if p->reg references the same register as v and v 733 // is a direct reference. 734 func copyau1(p *obj.Prog, v *obj.Addr) bool { 735 if regtyp(v) && v.Reg != 0 { 736 if p.Reg == v.Reg { 737 return true 738 } 739 } 740 return false 741 } 742 743 // copysub replaces v with s in a if f!=0 or indicates it if could if f==0. 744 // Returns 1 on failure to substitute (it always succeeds on mips). 745 func copysub(a *obj.Addr, v *obj.Addr, s *obj.Addr, f int) int { 746 if f != 0 { 747 if copyau(a, v) { 748 a.Reg = s.Reg 749 } 750 } 751 return 0 752 } 753 754 // copysub1 replaces v with s in p1->reg if f!=0 or indicates if it could if f==0. 755 // Returns 1 on failure to substitute (it always succeeds on mips). 756 func copysub1(p1 *obj.Prog, v *obj.Addr, s *obj.Addr, f int) int { 757 if f != 0 { 758 if copyau1(p1, v) { 759 p1.Reg = s.Reg 760 } 761 } 762 return 0 763 } 764 765 func sameaddr(a *obj.Addr, v *obj.Addr) bool { 766 if a.Type != v.Type { 767 return false 768 } 769 if regtyp(v) && a.Reg == v.Reg { 770 return true 771 } 772 if v.Type == obj.NAME_AUTO || v.Type == obj.NAME_PARAM { 773 if v.Offset == a.Offset { 774 return true 775 } 776 } 777 return false 778 } 779 780 func smallindir(a *obj.Addr, reg *obj.Addr) bool { 781 return reg.Type == obj.TYPE_REG && a.Type == obj.TYPE_MEM && a.Reg == reg.Reg && 0 <= a.Offset && a.Offset < 4096 782 } 783 784 func stackaddr(a *obj.Addr) bool { 785 return a.Type == obj.TYPE_REG && a.Reg == mips.REGSP 786 }