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