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