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