github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/cmd/internal/obj/arm64/obj7.go (about) 1 // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova. 2 // https://code.google.com/p/ken-cc/source/browse/ 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 arm64 32 33 import ( 34 "cmd/internal/obj" 35 "cmd/internal/sys" 36 "fmt" 37 "log" 38 "math" 39 ) 40 41 var complements = []obj.As{ 42 AADD: ASUB, 43 AADDW: ASUBW, 44 ASUB: AADD, 45 ASUBW: AADDW, 46 ACMP: ACMN, 47 ACMPW: ACMNW, 48 ACMN: ACMP, 49 ACMNW: ACMPW, 50 } 51 52 func stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 53 // MOV g_stackguard(g), R1 54 p = obj.Appendp(ctxt, p) 55 56 p.As = AMOVD 57 p.From.Type = obj.TYPE_MEM 58 p.From.Reg = REGG 59 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 60 if ctxt.Cursym.CFunc() { 61 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 62 } 63 p.To.Type = obj.TYPE_REG 64 p.To.Reg = REG_R1 65 66 q := (*obj.Prog)(nil) 67 if framesize <= obj.StackSmall { 68 // small stack: SP < stackguard 69 // MOV SP, R2 70 // CMP stackguard, R2 71 p = obj.Appendp(ctxt, p) 72 73 p.As = AMOVD 74 p.From.Type = obj.TYPE_REG 75 p.From.Reg = REGSP 76 p.To.Type = obj.TYPE_REG 77 p.To.Reg = REG_R2 78 79 p = obj.Appendp(ctxt, p) 80 p.As = ACMP 81 p.From.Type = obj.TYPE_REG 82 p.From.Reg = REG_R1 83 p.Reg = REG_R2 84 } else if framesize <= obj.StackBig { 85 // large stack: SP-framesize < stackguard-StackSmall 86 // SUB $framesize, SP, R2 87 // CMP stackguard, R2 88 p = obj.Appendp(ctxt, p) 89 90 p.As = ASUB 91 p.From.Type = obj.TYPE_CONST 92 p.From.Offset = int64(framesize) 93 p.Reg = REGSP 94 p.To.Type = obj.TYPE_REG 95 p.To.Reg = REG_R2 96 97 p = obj.Appendp(ctxt, p) 98 p.As = ACMP 99 p.From.Type = obj.TYPE_REG 100 p.From.Reg = REG_R1 101 p.Reg = REG_R2 102 } else { 103 // Such a large stack we need to protect against wraparound 104 // if SP is close to zero. 105 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 106 // The +StackGuard on both sides is required to keep the left side positive: 107 // SP is allowed to be slightly below stackguard. See stack.h. 108 // CMP $StackPreempt, R1 109 // BEQ label_of_call_to_morestack 110 // ADD $StackGuard, SP, R2 111 // SUB R1, R2 112 // MOV $(framesize+(StackGuard-StackSmall)), R3 113 // CMP R3, R2 114 p = obj.Appendp(ctxt, p) 115 116 p.As = ACMP 117 p.From.Type = obj.TYPE_CONST 118 p.From.Offset = obj.StackPreempt 119 p.Reg = REG_R1 120 121 p = obj.Appendp(ctxt, p) 122 q = p 123 p.As = ABEQ 124 p.To.Type = obj.TYPE_BRANCH 125 126 p = obj.Appendp(ctxt, p) 127 p.As = AADD 128 p.From.Type = obj.TYPE_CONST 129 p.From.Offset = obj.StackGuard 130 p.Reg = REGSP 131 p.To.Type = obj.TYPE_REG 132 p.To.Reg = REG_R2 133 134 p = obj.Appendp(ctxt, p) 135 p.As = ASUB 136 p.From.Type = obj.TYPE_REG 137 p.From.Reg = REG_R1 138 p.To.Type = obj.TYPE_REG 139 p.To.Reg = REG_R2 140 141 p = obj.Appendp(ctxt, p) 142 p.As = AMOVD 143 p.From.Type = obj.TYPE_CONST 144 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 145 p.To.Type = obj.TYPE_REG 146 p.To.Reg = REG_R3 147 148 p = obj.Appendp(ctxt, p) 149 p.As = ACMP 150 p.From.Type = obj.TYPE_REG 151 p.From.Reg = REG_R3 152 p.Reg = REG_R2 153 } 154 155 // BLS do-morestack 156 bls := obj.Appendp(ctxt, p) 157 bls.As = ABLS 158 bls.To.Type = obj.TYPE_BRANCH 159 160 var last *obj.Prog 161 for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link { 162 } 163 164 // Now we are at the end of the function, but logically 165 // we are still in function prologue. We need to fix the 166 // SP data and PCDATA. 167 spfix := obj.Appendp(ctxt, last) 168 spfix.As = obj.ANOP 169 spfix.Spadj = -framesize 170 171 pcdata := obj.Appendp(ctxt, spfix) 172 pcdata.Lineno = ctxt.Cursym.Text.Lineno 173 pcdata.Mode = ctxt.Cursym.Text.Mode 174 pcdata.As = obj.APCDATA 175 pcdata.From.Type = obj.TYPE_CONST 176 pcdata.From.Offset = obj.PCDATA_StackMapIndex 177 pcdata.To.Type = obj.TYPE_CONST 178 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 179 180 // MOV LR, R3 181 movlr := obj.Appendp(ctxt, pcdata) 182 movlr.As = AMOVD 183 movlr.From.Type = obj.TYPE_REG 184 movlr.From.Reg = REGLINK 185 movlr.To.Type = obj.TYPE_REG 186 movlr.To.Reg = REG_R3 187 if q != nil { 188 q.Pcond = movlr 189 } 190 bls.Pcond = movlr 191 192 debug := movlr 193 if false { 194 debug = obj.Appendp(ctxt, debug) 195 debug.As = AMOVD 196 debug.From.Type = obj.TYPE_CONST 197 debug.From.Offset = int64(framesize) 198 debug.To.Type = obj.TYPE_REG 199 debug.To.Reg = REGTMP 200 } 201 202 // BL runtime.morestack(SB) 203 call := obj.Appendp(ctxt, debug) 204 call.As = ABL 205 call.To.Type = obj.TYPE_BRANCH 206 morestack := "runtime.morestack" 207 switch { 208 case ctxt.Cursym.CFunc(): 209 morestack = "runtime.morestackc" 210 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 211 morestack = "runtime.morestack_noctxt" 212 } 213 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 214 215 // B start 216 jmp := obj.Appendp(ctxt, call) 217 jmp.As = AB 218 jmp.To.Type = obj.TYPE_BRANCH 219 jmp.Pcond = ctxt.Cursym.Text.Link 220 jmp.Spadj = +framesize 221 222 // placeholder for bls's jump target 223 // p = obj.Appendp(ctxt, p) 224 // p.As = obj.ANOP 225 226 return bls 227 } 228 229 func progedit(ctxt *obj.Link, p *obj.Prog) { 230 p.From.Class = 0 231 p.To.Class = 0 232 233 // $0 results in C_ZCON, which matches both C_REG and various 234 // C_xCON, however the C_REG cases in asmout don't expect a 235 // constant, so they will use the register fields and assemble 236 // a R0. To prevent that, rewrite $0 as ZR. 237 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 { 238 p.From.Type = obj.TYPE_REG 239 p.From.Reg = REGZERO 240 } 241 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 { 242 p.To.Type = obj.TYPE_REG 243 p.To.Reg = REGZERO 244 } 245 246 // Rewrite BR/BL to symbol as TYPE_BRANCH. 247 switch p.As { 248 case AB, 249 ABL, 250 obj.ARET, 251 obj.ADUFFZERO, 252 obj.ADUFFCOPY: 253 if p.To.Sym != nil { 254 p.To.Type = obj.TYPE_BRANCH 255 } 256 break 257 } 258 259 // Rewrite float constants to values stored in memory. 260 switch p.As { 261 case AFMOVS: 262 if p.From.Type == obj.TYPE_FCONST { 263 f32 := float32(p.From.Val.(float64)) 264 i32 := math.Float32bits(f32) 265 if i32 == 0 { 266 p.From.Type = obj.TYPE_REG 267 p.From.Reg = REGZERO 268 break 269 } 270 literal := fmt.Sprintf("$f32.%08x", i32) 271 s := obj.Linklookup(ctxt, literal, 0) 272 s.Size = 4 273 p.From.Type = obj.TYPE_MEM 274 p.From.Sym = s 275 p.From.Sym.Set(obj.AttrLocal, true) 276 p.From.Name = obj.NAME_EXTERN 277 p.From.Offset = 0 278 } 279 280 case AFMOVD: 281 if p.From.Type == obj.TYPE_FCONST { 282 i64 := math.Float64bits(p.From.Val.(float64)) 283 if i64 == 0 { 284 p.From.Type = obj.TYPE_REG 285 p.From.Reg = REGZERO 286 break 287 } 288 literal := fmt.Sprintf("$f64.%016x", i64) 289 s := obj.Linklookup(ctxt, literal, 0) 290 s.Size = 8 291 p.From.Type = obj.TYPE_MEM 292 p.From.Sym = s 293 p.From.Sym.Set(obj.AttrLocal, true) 294 p.From.Name = obj.NAME_EXTERN 295 p.From.Offset = 0 296 } 297 298 break 299 } 300 301 // Rewrite negative immediates as positive immediates with 302 // complementary instruction. 303 switch p.As { 304 case AADD, ASUB, ACMP, ACMN: 305 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 { 306 p.From.Offset = -p.From.Offset 307 p.As = complements[p.As] 308 } 309 case AADDW, ASUBW, ACMPW, ACMNW: 310 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 { 311 p.From.Offset = -p.From.Offset 312 p.As = complements[p.As] 313 } 314 } 315 316 // For 32-bit logical instruction with constant, 317 // rewrite the high 32-bit to be a repetition of 318 // the low 32-bit, so that the BITCON test can be 319 // shared for both 32-bit and 64-bit. 32-bit ops 320 // will zero the high 32-bit of the destination 321 // register anyway. 322 switch p.As { 323 case AANDW, AORRW, AEORW, AANDSW: 324 if p.From.Type == obj.TYPE_CONST { 325 v := p.From.Offset & 0xffffffff 326 p.From.Offset = v | v<<32 327 } 328 } 329 330 if ctxt.Flag_dynlink { 331 rewriteToUseGot(ctxt, p) 332 } 333 } 334 335 // Rewrite p, if necessary, to access global data via the global offset table. 336 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 337 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 338 // ADUFFxxx $offset 339 // becomes 340 // MOVD runtime.duffxxx@GOT, REGTMP 341 // ADD $offset, REGTMP 342 // CALL REGTMP 343 var sym *obj.LSym 344 if p.As == obj.ADUFFZERO { 345 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 346 } else { 347 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 348 } 349 offset := p.To.Offset 350 p.As = AMOVD 351 p.From.Type = obj.TYPE_MEM 352 p.From.Name = obj.NAME_GOTREF 353 p.From.Sym = sym 354 p.To.Type = obj.TYPE_REG 355 p.To.Reg = REGTMP 356 p.To.Name = obj.NAME_NONE 357 p.To.Offset = 0 358 p.To.Sym = nil 359 p1 := obj.Appendp(ctxt, p) 360 p1.As = AADD 361 p1.From.Type = obj.TYPE_CONST 362 p1.From.Offset = offset 363 p1.To.Type = obj.TYPE_REG 364 p1.To.Reg = REGTMP 365 p2 := obj.Appendp(ctxt, p1) 366 p2.As = obj.ACALL 367 p2.To.Type = obj.TYPE_REG 368 p2.To.Reg = REGTMP 369 } 370 371 // We only care about global data: NAME_EXTERN means a global 372 // symbol in the Go sense, and p.Sym.Local is true for a few 373 // internally defined symbols. 374 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 375 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 376 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 377 if p.As != AMOVD { 378 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 379 } 380 if p.To.Type != obj.TYPE_REG { 381 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 382 } 383 p.From.Type = obj.TYPE_MEM 384 p.From.Name = obj.NAME_GOTREF 385 if p.From.Offset != 0 { 386 q := obj.Appendp(ctxt, p) 387 q.As = AADD 388 q.From.Type = obj.TYPE_CONST 389 q.From.Offset = p.From.Offset 390 q.To = p.To 391 p.From.Offset = 0 392 } 393 } 394 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 395 ctxt.Diag("don't know how to handle %v with -dynlink", p) 396 } 397 var source *obj.Addr 398 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 399 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP) 400 // An addition may be inserted between the two MOVs if there is an offset. 401 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 402 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 403 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 404 } 405 source = &p.From 406 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 407 source = &p.To 408 } else { 409 return 410 } 411 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 412 return 413 } 414 if source.Sym.Type == obj.STLSBSS { 415 return 416 } 417 if source.Type != obj.TYPE_MEM { 418 ctxt.Diag("don't know how to handle %v with -dynlink", p) 419 } 420 p1 := obj.Appendp(ctxt, p) 421 p2 := obj.Appendp(ctxt, p1) 422 p1.As = AMOVD 423 p1.From.Type = obj.TYPE_MEM 424 p1.From.Sym = source.Sym 425 p1.From.Name = obj.NAME_GOTREF 426 p1.To.Type = obj.TYPE_REG 427 p1.To.Reg = REGTMP 428 429 p2.As = p.As 430 p2.From = p.From 431 p2.To = p.To 432 if p.From.Name == obj.NAME_EXTERN { 433 p2.From.Reg = REGTMP 434 p2.From.Name = obj.NAME_NONE 435 p2.From.Sym = nil 436 } else if p.To.Name == obj.NAME_EXTERN { 437 p2.To.Reg = REGTMP 438 p2.To.Name = obj.NAME_NONE 439 p2.To.Sym = nil 440 } else { 441 return 442 } 443 obj.Nopout(p) 444 } 445 446 func follow(ctxt *obj.Link, s *obj.LSym) { 447 ctxt.Cursym = s 448 449 firstp := ctxt.NewProg() 450 lastp := firstp 451 xfol(ctxt, s.Text, &lastp) 452 lastp.Link = nil 453 s.Text = firstp.Link 454 } 455 456 func relinv(a obj.As) obj.As { 457 switch a { 458 case ABEQ: 459 return ABNE 460 case ABNE: 461 return ABEQ 462 case ABCS: 463 return ABCC 464 case ABHS: 465 return ABLO 466 case ABCC: 467 return ABCS 468 case ABLO: 469 return ABHS 470 case ABMI: 471 return ABPL 472 case ABPL: 473 return ABMI 474 case ABVS: 475 return ABVC 476 case ABVC: 477 return ABVS 478 case ABHI: 479 return ABLS 480 case ABLS: 481 return ABHI 482 case ABGE: 483 return ABLT 484 case ABLT: 485 return ABGE 486 case ABGT: 487 return ABLE 488 case ABLE: 489 return ABGT 490 case ACBZ: 491 return ACBNZ 492 case ACBNZ: 493 return ACBZ 494 case ACBZW: 495 return ACBNZW 496 case ACBNZW: 497 return ACBZW 498 } 499 500 log.Fatalf("unknown relation: %s", Anames[a-obj.ABaseARM64]) 501 return 0 502 } 503 504 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 505 var q *obj.Prog 506 var r *obj.Prog 507 var i int 508 509 loop: 510 if p == nil { 511 return 512 } 513 a := p.As 514 if a == AB { 515 q = p.Pcond 516 if q != nil { 517 p.Mark |= FOLL 518 p = q 519 if !(p.Mark&FOLL != 0) { 520 goto loop 521 } 522 } 523 } 524 525 if p.Mark&FOLL != 0 { 526 i = 0 527 q = p 528 for ; i < 4; i, q = i+1, q.Link { 529 if q == *last || q == nil { 530 break 531 } 532 a = q.As 533 if a == obj.ANOP { 534 i-- 535 continue 536 } 537 538 if a == AB || a == obj.ARET || a == AERET { 539 goto copy 540 } 541 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 542 continue 543 } 544 if a != ABEQ && a != ABNE { 545 continue 546 } 547 548 copy: 549 for { 550 r = ctxt.NewProg() 551 *r = *p 552 if !(r.Mark&FOLL != 0) { 553 fmt.Printf("can't happen 1\n") 554 } 555 r.Mark |= FOLL 556 if p != q { 557 p = p.Link 558 (*last).Link = r 559 *last = r 560 continue 561 } 562 563 (*last).Link = r 564 *last = r 565 if a == AB || a == obj.ARET || a == AERET { 566 return 567 } 568 if a == ABNE { 569 r.As = ABEQ 570 } else { 571 r.As = ABNE 572 } 573 r.Pcond = p.Link 574 r.Link = p.Pcond 575 if !(r.Link.Mark&FOLL != 0) { 576 xfol(ctxt, r.Link, last) 577 } 578 if !(r.Pcond.Mark&FOLL != 0) { 579 fmt.Printf("can't happen 2\n") 580 } 581 return 582 } 583 } 584 585 a = AB 586 q = ctxt.NewProg() 587 q.As = a 588 q.Lineno = p.Lineno 589 q.To.Type = obj.TYPE_BRANCH 590 q.To.Offset = p.Pc 591 q.Pcond = p 592 p = q 593 } 594 595 p.Mark |= FOLL 596 (*last).Link = p 597 *last = p 598 if a == AB || a == obj.ARET || a == AERET { 599 return 600 } 601 if p.Pcond != nil { 602 if a != ABL && p.Link != nil { 603 q = obj.Brchain(ctxt, p.Link) 604 if a != obj.ATEXT { 605 if q != nil && (q.Mark&FOLL != 0) { 606 p.As = relinv(a) 607 p.Link = p.Pcond 608 p.Pcond = q 609 } 610 } 611 612 xfol(ctxt, p.Link, last) 613 q = obj.Brchain(ctxt, p.Pcond) 614 if q == nil { 615 q = p.Pcond 616 } 617 if q.Mark&FOLL != 0 { 618 p.Pcond = q 619 return 620 } 621 622 p = q 623 goto loop 624 } 625 } 626 627 p = p.Link 628 goto loop 629 } 630 631 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 632 ctxt.Cursym = cursym 633 634 if cursym.Text == nil || cursym.Text.Link == nil { 635 return 636 } 637 638 p := cursym.Text 639 textstksiz := p.To.Offset 640 aoffset := int32(textstksiz) 641 642 cursym.Args = p.To.Val.(int32) 643 cursym.Locals = int32(textstksiz) 644 645 /* 646 * find leaf subroutines 647 * strip NOPs 648 * expand RET 649 */ 650 q := (*obj.Prog)(nil) 651 var q1 *obj.Prog 652 for p := cursym.Text; p != nil; p = p.Link { 653 switch p.As { 654 case obj.ATEXT: 655 p.Mark |= LEAF 656 657 case obj.ARET: 658 break 659 660 case obj.ANOP: 661 q1 = p.Link 662 q.Link = q1 /* q is non-nop */ 663 q1.Mark |= p.Mark 664 continue 665 666 case ABL, 667 obj.ADUFFZERO, 668 obj.ADUFFCOPY: 669 cursym.Text.Mark &^= LEAF 670 fallthrough 671 672 case ACBNZ, 673 ACBZ, 674 ACBNZW, 675 ACBZW, 676 ATBZ, 677 ATBNZ, 678 AB, 679 ABEQ, 680 ABNE, 681 ABCS, 682 ABHS, 683 ABCC, 684 ABLO, 685 ABMI, 686 ABPL, 687 ABVS, 688 ABVC, 689 ABHI, 690 ABLS, 691 ABGE, 692 ABLT, 693 ABGT, 694 ABLE, 695 AADR, /* strange */ 696 AADRP: 697 q1 = p.Pcond 698 699 if q1 != nil { 700 for q1.As == obj.ANOP { 701 q1 = q1.Link 702 p.Pcond = q1 703 } 704 } 705 706 break 707 } 708 709 q = p 710 } 711 712 var q2 *obj.Prog 713 var retjmp *obj.LSym 714 for p := cursym.Text; p != nil; p = p.Link { 715 o := p.As 716 switch o { 717 case obj.ATEXT: 718 cursym.Text = p 719 if textstksiz < 0 { 720 ctxt.Autosize = 0 721 } else { 722 ctxt.Autosize = int32(textstksiz + 8) 723 } 724 if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 { 725 ctxt.Autosize = 0 726 } else if ctxt.Autosize&(16-1) != 0 { 727 // The frame includes an LR. 728 // If the frame size is 8, it's only an LR, 729 // so there's no potential for breaking references to 730 // local variables by growing the frame size, 731 // because there are no local variables. 732 // But otherwise, if there is a non-empty locals section, 733 // the author of the code is responsible for making sure 734 // that the frame size is 8 mod 16. 735 if ctxt.Autosize == 8 { 736 ctxt.Autosize += 8 737 cursym.Locals += 8 738 } else { 739 ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8) 740 } 741 } 742 p.To.Offset = int64(ctxt.Autosize) - 8 743 if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) { 744 if ctxt.Debugvlog != 0 { 745 ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name) 746 } 747 cursym.Text.Mark |= LEAF 748 } 749 750 if !(p.From3.Offset&obj.NOSPLIT != 0) { 751 p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check 752 } 753 754 aoffset = ctxt.Autosize 755 if aoffset > 0xF0 { 756 aoffset = 0xF0 757 } 758 if cursym.Text.Mark&LEAF != 0 { 759 cursym.Set(obj.AttrLeaf, true) 760 if ctxt.Autosize == 0 { 761 break 762 } 763 } 764 765 // Frame is non-empty. Make sure to save link register, even if 766 // it is a leaf function, so that traceback works. 767 q = p 768 if ctxt.Autosize > aoffset { 769 // Frame size is too large for a MOVD.W instruction. 770 // Store link register before decrementing SP, so if a signal comes 771 // during the execution of the function prologue, the traceback 772 // code will not see a half-updated stack frame. 773 q = obj.Appendp(ctxt, q) 774 q.Lineno = p.Lineno 775 q.As = ASUB 776 q.From.Type = obj.TYPE_CONST 777 q.From.Offset = int64(ctxt.Autosize) 778 q.Reg = REGSP 779 q.To.Type = obj.TYPE_REG 780 q.To.Reg = REGTMP 781 782 q = obj.Appendp(ctxt, q) 783 q.Lineno = p.Lineno 784 q.As = AMOVD 785 q.From.Type = obj.TYPE_REG 786 q.From.Reg = REGLINK 787 q.To.Type = obj.TYPE_MEM 788 q.To.Reg = REGTMP 789 790 q1 = obj.Appendp(ctxt, q) 791 q1.Lineno = p.Lineno 792 q1.As = AMOVD 793 q1.From.Type = obj.TYPE_REG 794 q1.From.Reg = REGTMP 795 q1.To.Type = obj.TYPE_REG 796 q1.To.Reg = REGSP 797 q1.Spadj = ctxt.Autosize 798 } else { 799 // small frame, update SP and save LR in a single MOVD.W instruction 800 q1 = obj.Appendp(ctxt, q) 801 q1.As = AMOVD 802 q1.Lineno = p.Lineno 803 q1.From.Type = obj.TYPE_REG 804 q1.From.Reg = REGLINK 805 q1.To.Type = obj.TYPE_MEM 806 q1.Scond = C_XPRE 807 q1.To.Offset = int64(-aoffset) 808 q1.To.Reg = REGSP 809 q1.Spadj = aoffset 810 } 811 812 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 813 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 814 // 815 // MOV g_panic(g), R1 816 // CMP ZR, R1 817 // BEQ end 818 // MOV panic_argp(R1), R2 819 // ADD $(autosize+8), RSP, R3 820 // CMP R2, R3 821 // BNE end 822 // ADD $8, RSP, R4 823 // MOVD R4, panic_argp(R1) 824 // end: 825 // NOP 826 // 827 // The NOP is needed to give the jumps somewhere to land. 828 // It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes. 829 q = q1 830 831 q = obj.Appendp(ctxt, q) 832 q.As = AMOVD 833 q.From.Type = obj.TYPE_MEM 834 q.From.Reg = REGG 835 q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 836 q.To.Type = obj.TYPE_REG 837 q.To.Reg = REG_R1 838 839 q = obj.Appendp(ctxt, q) 840 q.As = ACMP 841 q.From.Type = obj.TYPE_REG 842 q.From.Reg = REGZERO 843 q.Reg = REG_R1 844 845 q = obj.Appendp(ctxt, q) 846 q.As = ABEQ 847 q.To.Type = obj.TYPE_BRANCH 848 q1 = q 849 850 q = obj.Appendp(ctxt, q) 851 q.As = AMOVD 852 q.From.Type = obj.TYPE_MEM 853 q.From.Reg = REG_R1 854 q.From.Offset = 0 // Panic.argp 855 q.To.Type = obj.TYPE_REG 856 q.To.Reg = REG_R2 857 858 q = obj.Appendp(ctxt, q) 859 q.As = AADD 860 q.From.Type = obj.TYPE_CONST 861 q.From.Offset = int64(ctxt.Autosize) + 8 862 q.Reg = REGSP 863 q.To.Type = obj.TYPE_REG 864 q.To.Reg = REG_R3 865 866 q = obj.Appendp(ctxt, q) 867 q.As = ACMP 868 q.From.Type = obj.TYPE_REG 869 q.From.Reg = REG_R2 870 q.Reg = REG_R3 871 872 q = obj.Appendp(ctxt, q) 873 q.As = ABNE 874 q.To.Type = obj.TYPE_BRANCH 875 q2 = q 876 877 q = obj.Appendp(ctxt, q) 878 q.As = AADD 879 q.From.Type = obj.TYPE_CONST 880 q.From.Offset = 8 881 q.Reg = REGSP 882 q.To.Type = obj.TYPE_REG 883 q.To.Reg = REG_R4 884 885 q = obj.Appendp(ctxt, q) 886 q.As = AMOVD 887 q.From.Type = obj.TYPE_REG 888 q.From.Reg = REG_R4 889 q.To.Type = obj.TYPE_MEM 890 q.To.Reg = REG_R1 891 q.To.Offset = 0 // Panic.argp 892 893 q = obj.Appendp(ctxt, q) 894 895 q.As = obj.ANOP 896 q1.Pcond = q 897 q2.Pcond = q 898 } 899 900 case obj.ARET: 901 nocache(p) 902 if p.From.Type == obj.TYPE_CONST { 903 ctxt.Diag("using BECOME (%v) is not supported!", p) 904 break 905 } 906 907 retjmp = p.To.Sym 908 p.To = obj.Addr{} 909 if cursym.Text.Mark&LEAF != 0 { 910 if ctxt.Autosize != 0 { 911 p.As = AADD 912 p.From.Type = obj.TYPE_CONST 913 p.From.Offset = int64(ctxt.Autosize) 914 p.To.Type = obj.TYPE_REG 915 p.To.Reg = REGSP 916 p.Spadj = -ctxt.Autosize 917 } 918 } else { 919 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ 920 aoffset = ctxt.Autosize 921 922 if aoffset > 0xF0 { 923 aoffset = 0xF0 924 } 925 p.As = AMOVD 926 p.From.Type = obj.TYPE_MEM 927 p.Scond = C_XPOST 928 p.From.Offset = int64(aoffset) 929 p.From.Reg = REGSP 930 p.To.Type = obj.TYPE_REG 931 p.To.Reg = REGLINK 932 p.Spadj = -aoffset 933 if ctxt.Autosize > aoffset { 934 q = ctxt.NewProg() 935 q.As = AADD 936 q.From.Type = obj.TYPE_CONST 937 q.From.Offset = int64(ctxt.Autosize) - int64(aoffset) 938 q.To.Type = obj.TYPE_REG 939 q.To.Reg = REGSP 940 q.Link = p.Link 941 q.Spadj = int32(-q.From.Offset) 942 q.Lineno = p.Lineno 943 p.Link = q 944 p = q 945 } 946 } 947 948 if p.As != obj.ARET { 949 q = ctxt.NewProg() 950 q.Lineno = p.Lineno 951 q.Link = p.Link 952 p.Link = q 953 p = q 954 } 955 956 if retjmp != nil { // retjmp 957 p.As = AB 958 p.To.Type = obj.TYPE_BRANCH 959 p.To.Sym = retjmp 960 p.Spadj = +ctxt.Autosize 961 break 962 } 963 964 p.As = obj.ARET 965 p.To.Type = obj.TYPE_MEM 966 p.To.Offset = 0 967 p.To.Reg = REGLINK 968 p.Spadj = +ctxt.Autosize 969 970 case AADD, ASUB: 971 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 972 if p.As == AADD { 973 p.Spadj = int32(-p.From.Offset) 974 } else { 975 p.Spadj = int32(+p.From.Offset) 976 } 977 } 978 break 979 } 980 } 981 } 982 983 func nocache(p *obj.Prog) { 984 p.Optab = 0 985 p.From.Class = 0 986 p.To.Class = 0 987 } 988 989 var unaryDst = map[obj.As]bool{ 990 AWORD: true, 991 ADWORD: true, 992 ABL: true, 993 AB: true, 994 ASVC: true, 995 } 996 997 var Linkarm64 = obj.LinkArch{ 998 Arch: sys.ArchARM64, 999 Preprocess: preprocess, 1000 Assemble: span7, 1001 Follow: follow, 1002 Progedit: progedit, 1003 UnaryDst: unaryDst, 1004 }