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