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 ([email protected]) 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 ([email protected]) 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 31package arm64 32 33import ( 34 "cmd/internal/obj" 35 "cmd/internal/objabi" 36 "cmd/internal/src" 37 "cmd/internal/sys" 38 "internal/abi" 39 "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. 46var 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 67func (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 306func 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. 402func (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 512func 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 for p := c.cursym.Func().Text; p != nil; p = p.Link { 556 o := p.As 557 switch o { 558 case obj.ATEXT: 559 c.cursym.Func().Text = p 560 c.autosize = int32(textstksiz) 561 562 if p.Mark&LEAF != 0 && c.autosize == 0 { 563 // A leaf function with no locals has no frame. 564 p.From.Sym.Set(obj.AttrNoFrame, true) 565 } 566 567 if !p.From.Sym.NoFrame() { 568 // If there is a stack frame at all, it includes 569 // space to save the LR. 570 c.autosize += 8 571 } 572 573 if c.autosize != 0 { 574 extrasize := int32(0) 575 if c.autosize%16 == 8 { 576 // Allocate extra 8 bytes on the frame top to save FP 577 extrasize = 8 578 } else if c.autosize&(16-1) == 0 { 579 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16 580 extrasize = 16 581 } else { 582 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8) 583 } 584 c.autosize += extrasize 585 c.cursym.Func().Locals += extrasize 586 587 // low 32 bits for autosize 588 // high 32 bits for extrasize 589 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32 590 } else { 591 // NOFRAME 592 p.To.Offset = 0 593 } 594 595 if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 { 596 if c.ctxt.Debugvlog { 597 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name) 598 } 599 c.cursym.Func().Text.Mark |= LEAF 600 } 601 602 if cursym.Func().Text.Mark&LEAF != 0 { 603 cursym.Set(obj.AttrLeaf, true) 604 if p.From.Sym.NoFrame() { 605 break 606 } 607 } 608 609 if p.Mark&LEAF != 0 && c.autosize < abi.StackSmall { 610 // A leaf function with a small stack can be marked 611 // NOSPLIT, avoiding a stack check. 612 p.From.Sym.Set(obj.AttrNoSplit, true) 613 } 614 615 if !p.From.Sym.NoSplit() { 616 p = c.stacksplit(p, c.autosize) // emit split check 617 } 618 619 var prologueEnd *obj.Prog 620 621 aoffset := c.autosize 622 if aoffset > 0xf0 { 623 // MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned. 624 // so the maximum aoffset value is 0xf0. 625 aoffset = 0xf0 626 } 627 628 // Frame is non-empty. Make sure to save link register, even if 629 // it is a leaf function, so that traceback works. 630 q = p 631 if c.autosize > aoffset { 632 // Frame size is too large for a MOVD.W instruction. Store the frame pointer 633 // register and link register before decrementing SP, so if a signal comes 634 // during the execution of the function prologue, the traceback code will 635 // not see a half-updated stack frame. 636 637 // SUB $autosize, RSP, R20 638 q1 = obj.Appendp(q, c.newprog) 639 q1.Pos = p.Pos 640 q1.As = ASUB 641 q1.From.Type = obj.TYPE_CONST 642 q1.From.Offset = int64(c.autosize) 643 q1.Reg = REGSP 644 q1.To.Type = obj.TYPE_REG 645 q1.To.Reg = REG_R20 646 647 prologueEnd = q1 648 649 // STP (R29, R30), -8(R20) 650 q1 = obj.Appendp(q1, c.newprog) 651 q1.Pos = p.Pos 652 q1.As = ASTP 653 q1.From.Type = obj.TYPE_REGREG 654 q1.From.Reg = REGFP 655 q1.From.Offset = REGLINK 656 q1.To.Type = obj.TYPE_MEM 657 q1.To.Reg = REG_R20 658 q1.To.Offset = -8 659 660 // This is not async preemptible, as if we open a frame 661 // at the current SP, it will clobber the saved LR. 662 q1 = c.ctxt.StartUnsafePoint(q1, c.newprog) 663 664 // MOVD R20, RSP 665 q1 = obj.Appendp(q1, c.newprog) 666 q1.Pos = p.Pos 667 q1.As = AMOVD 668 q1.From.Type = obj.TYPE_REG 669 q1.From.Reg = REG_R20 670 q1.To.Type = obj.TYPE_REG 671 q1.To.Reg = REGSP 672 q1.Spadj = c.autosize 673 674 q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1) 675 676 if buildcfg.GOOS == "ios" { 677 // iOS does not support SA_ONSTACK. We will run the signal handler 678 // on the G stack. If we write below SP, it may be clobbered by 679 // the signal handler. So we save FP and LR after decrementing SP. 680 // STP (R29, R30), -8(RSP) 681 q1 = obj.Appendp(q1, c.newprog) 682 q1.Pos = p.Pos 683 q1.As = ASTP 684 q1.From.Type = obj.TYPE_REGREG 685 q1.From.Reg = REGFP 686 q1.From.Offset = REGLINK 687 q1.To.Type = obj.TYPE_MEM 688 q1.To.Reg = REGSP 689 q1.To.Offset = -8 690 } 691 } else { 692 // small frame, update SP and save LR in a single MOVD.W instruction. 693 // So if a signal comes during the execution of the function prologue, 694 // the traceback code will not see a half-updated stack frame. 695 // Also, on Linux, in a cgo binary we may get a SIGSETXID signal 696 // early on before the signal stack is set, as glibc doesn't allow 697 // us to block SIGSETXID. So it is important that we don't write below 698 // the SP until the signal stack is set. 699 // Luckily, all the functions from thread entry to setting the signal 700 // stack have small frames. 701 q1 = obj.Appendp(q, c.newprog) 702 q1.As = AMOVD 703 q1.Pos = p.Pos 704 q1.From.Type = obj.TYPE_REG 705 q1.From.Reg = REGLINK 706 q1.To.Type = obj.TYPE_MEM 707 q1.Scond = C_XPRE 708 q1.To.Offset = int64(-aoffset) 709 q1.To.Reg = REGSP 710 q1.Spadj = aoffset 711 712 prologueEnd = q1 713 714 // Frame pointer. 715 q1 = obj.Appendp(q1, c.newprog) 716 q1.Pos = p.Pos 717 q1.As = AMOVD 718 q1.From.Type = obj.TYPE_REG 719 q1.From.Reg = REGFP 720 q1.To.Type = obj.TYPE_MEM 721 q1.To.Reg = REGSP 722 q1.To.Offset = -8 723 } 724 725 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd) 726 727 q1 = obj.Appendp(q1, c.newprog) 728 q1.Pos = p.Pos 729 q1.As = ASUB 730 q1.From.Type = obj.TYPE_CONST 731 q1.From.Offset = 8 732 q1.Reg = REGSP 733 q1.To.Type = obj.TYPE_REG 734 q1.To.Reg = REGFP 735 736 if c.cursym.Func().Text.From.Sym.Wrapper() { 737 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 738 // 739 // MOV g_panic(g), RT1 740 // CBNZ checkargp 741 // end: 742 // NOP 743 // ... function body ... 744 // checkargp: 745 // MOV panic_argp(RT1), RT2 746 // ADD $(autosize+8), RSP, R20 747 // CMP RT2, R20 748 // BNE end 749 // ADD $8, RSP, R20 750 // MOVD R20, panic_argp(RT1) 751 // B end 752 // 753 // The NOP is needed to give the jumps somewhere to land. 754 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes. 755 q = q1 756 757 // MOV g_panic(g), RT1 758 q = obj.Appendp(q, c.newprog) 759 q.As = AMOVD 760 q.From.Type = obj.TYPE_MEM 761 q.From.Reg = REGG 762 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 763 q.To.Type = obj.TYPE_REG 764 q.To.Reg = REGRT1 765 766 // CBNZ RT1, checkargp 767 cbnz := obj.Appendp(q, c.newprog) 768 cbnz.As = ACBNZ 769 cbnz.From.Type = obj.TYPE_REG 770 cbnz.From.Reg = REGRT1 771 cbnz.To.Type = obj.TYPE_BRANCH 772 773 // Empty branch target at the top of the function body 774 end := obj.Appendp(cbnz, c.newprog) 775 end.As = obj.ANOP 776 777 // find the end of the function 778 var last *obj.Prog 779 for last = end; last.Link != nil; last = last.Link { 780 } 781 782 // MOV panic_argp(RT1), RT2 783 mov := obj.Appendp(last, c.newprog) 784 mov.As = AMOVD 785 mov.From.Type = obj.TYPE_MEM 786 mov.From.Reg = REGRT1 787 mov.From.Offset = 0 // Panic.argp 788 mov.To.Type = obj.TYPE_REG 789 mov.To.Reg = REGRT2 790 791 // CBNZ branches to the MOV above 792 cbnz.To.SetTarget(mov) 793 794 // ADD $(autosize+8), SP, R20 795 q = obj.Appendp(mov, c.newprog) 796 q.As = AADD 797 q.From.Type = obj.TYPE_CONST 798 q.From.Offset = int64(c.autosize) + 8 799 q.Reg = REGSP 800 q.To.Type = obj.TYPE_REG 801 q.To.Reg = REG_R20 802 803 // CMP RT2, R20 804 q = obj.Appendp(q, c.newprog) 805 q.As = ACMP 806 q.From.Type = obj.TYPE_REG 807 q.From.Reg = REGRT2 808 q.Reg = REG_R20 809 810 // BNE end 811 q = obj.Appendp(q, c.newprog) 812 q.As = ABNE 813 q.To.Type = obj.TYPE_BRANCH 814 q.To.SetTarget(end) 815 816 // ADD $8, SP, R20 817 q = obj.Appendp(q, c.newprog) 818 q.As = AADD 819 q.From.Type = obj.TYPE_CONST 820 q.From.Offset = 8 821 q.Reg = REGSP 822 q.To.Type = obj.TYPE_REG 823 q.To.Reg = REG_R20 824 825 // MOV R20, panic_argp(RT1) 826 q = obj.Appendp(q, c.newprog) 827 q.As = AMOVD 828 q.From.Type = obj.TYPE_REG 829 q.From.Reg = REG_R20 830 q.To.Type = obj.TYPE_MEM 831 q.To.Reg = REGRT1 832 q.To.Offset = 0 // Panic.argp 833 834 // B end 835 q = obj.Appendp(q, c.newprog) 836 q.As = AB 837 q.To.Type = obj.TYPE_BRANCH 838 q.To.SetTarget(end) 839 } 840 841 case obj.ARET: 842 nocache(p) 843 if p.From.Type == obj.TYPE_CONST { 844 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 845 break 846 } 847 848 retJMP, retReg := p.To.Sym, p.To.Reg 849 if retReg == 0 { 850 retReg = REGLINK 851 } 852 p.To = obj.Addr{} 853 if c.cursym.Func().Text.Mark&LEAF != 0 { 854 if c.autosize != 0 { 855 // Restore frame pointer. 856 // ADD $framesize-8, RSP, R29 857 p.As = AADD 858 p.From.Type = obj.TYPE_CONST 859 p.From.Offset = int64(c.autosize) - 8 860 p.Reg = REGSP 861 p.To.Type = obj.TYPE_REG 862 p.To.Reg = REGFP 863 864 // Pop stack frame. 865 // ADD $framesize, RSP, RSP 866 p = obj.Appendp(p, c.newprog) 867 p.As = AADD 868 p.From.Type = obj.TYPE_CONST 869 p.From.Offset = int64(c.autosize) 870 p.To.Type = obj.TYPE_REG 871 p.To.Reg = REGSP 872 p.Spadj = -c.autosize 873 } 874 } else { 875 aoffset := c.autosize 876 // LDP -8(RSP), (R29, R30) 877 p.As = ALDP 878 p.From.Type = obj.TYPE_MEM 879 p.From.Offset = -8 880 p.From.Reg = REGSP 881 p.To.Type = obj.TYPE_REGREG 882 p.To.Reg = REGFP 883 p.To.Offset = REGLINK 884 885 // ADD $aoffset, RSP, RSP 886 q = newprog() 887 q.As = AADD 888 q.From.Type = obj.TYPE_CONST 889 q.From.Offset = int64(aoffset) 890 q.To.Type = obj.TYPE_REG 891 q.To.Reg = REGSP 892 q.Spadj = -aoffset 893 q.Pos = p.Pos 894 q.Link = p.Link 895 p.Link = q 896 p = q 897 } 898 899 // If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC', 900 // so that if you are debugging a low-level crash where PC and LR are zero, 901 // you can look at R27 to see what jumped to the zero. 902 // This is useful when bringing up Go on a new system. 903 // (There is similar code in ../ppc64/obj9.go:/if.false.) 904 const debugRETZERO = false 905 if debugRETZERO { 906 if p.As != obj.ARET { 907 q = newprog() 908 q.Pos = p.Pos 909 q.Link = p.Link 910 p.Link = q 911 p = q 912 } 913 p.As = AADR 914 p.From.Type = obj.TYPE_BRANCH 915 p.From.Offset = 0 916 p.To.Type = obj.TYPE_REG 917 p.To.Reg = REGTMP 918 919 } 920 921 if p.As != obj.ARET { 922 q = newprog() 923 q.Pos = p.Pos 924 q.Link = p.Link 925 p.Link = q 926 p = q 927 } 928 929 if retJMP != nil { 930 p.As = AB 931 p.To.Type = obj.TYPE_BRANCH 932 p.To.Sym = retJMP 933 p.Spadj = +c.autosize 934 break 935 } 936 937 p.As = obj.ARET 938 p.To.Type = obj.TYPE_MEM 939 p.To.Offset = 0 940 p.To.Reg = retReg 941 p.Spadj = +c.autosize 942 943 case AADD, ASUB: 944 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 945 if p.As == AADD { 946 p.Spadj = int32(-p.From.Offset) 947 } else { 948 p.Spadj = int32(+p.From.Offset) 949 } 950 } 951 952 case obj.AGETCALLERPC: 953 if cursym.Leaf() { 954 /* MOVD LR, Rd */ 955 p.As = AMOVD 956 p.From.Type = obj.TYPE_REG 957 p.From.Reg = REGLINK 958 } else { 959 /* MOVD (RSP), Rd */ 960 p.As = AMOVD 961 p.From.Type = obj.TYPE_MEM 962 p.From.Reg = REGSP 963 } 964 965 case obj.ADUFFCOPY: 966 // ADR ret_addr, R27 967 // STP (FP, R27), -24(SP) 968 // SUB 24, SP, FP 969 // DUFFCOPY 970 // ret_addr: 971 // SUB 8, SP, FP 972 973 q1 := p 974 // copy DUFFCOPY from q1 to q4 975 q4 := obj.Appendp(p, c.newprog) 976 q4.Pos = p.Pos 977 q4.As = obj.ADUFFCOPY 978 q4.To = p.To 979 980 q1.As = AADR 981 q1.From.Type = obj.TYPE_BRANCH 982 q1.To.Type = obj.TYPE_REG 983 q1.To.Reg = REG_R27 984 985 q2 := obj.Appendp(q1, c.newprog) 986 q2.Pos = p.Pos 987 q2.As = ASTP 988 q2.From.Type = obj.TYPE_REGREG 989 q2.From.Reg = REGFP 990 q2.From.Offset = int64(REG_R27) 991 q2.To.Type = obj.TYPE_MEM 992 q2.To.Reg = REGSP 993 q2.To.Offset = -24 994 995 // maintain FP for DUFFCOPY 996 q3 := obj.Appendp(q2, c.newprog) 997 q3.Pos = p.Pos 998 q3.As = ASUB 999 q3.From.Type = obj.TYPE_CONST 1000 q3.From.Offset = 24 1001 q3.Reg = REGSP 1002 q3.To.Type = obj.TYPE_REG 1003 q3.To.Reg = REGFP 1004 1005 q5 := obj.Appendp(q4, c.newprog) 1006 q5.Pos = p.Pos 1007 q5.As = ASUB 1008 q5.From.Type = obj.TYPE_CONST 1009 q5.From.Offset = 8 1010 q5.Reg = REGSP 1011 q5.To.Type = obj.TYPE_REG 1012 q5.To.Reg = REGFP 1013 q1.From.SetTarget(q5) 1014 p = q5 1015 1016 case obj.ADUFFZERO: 1017 // ADR ret_addr, R27 1018 // STP (FP, R27), -24(SP) 1019 // SUB 24, SP, FP 1020 // DUFFZERO 1021 // ret_addr: 1022 // SUB 8, SP, FP 1023 1024 q1 := p 1025 // copy DUFFZERO from q1 to q4 1026 q4 := obj.Appendp(p, c.newprog) 1027 q4.Pos = p.Pos 1028 q4.As = obj.ADUFFZERO 1029 q4.To = p.To 1030 1031 q1.As = AADR 1032 q1.From.Type = obj.TYPE_BRANCH 1033 q1.To.Type = obj.TYPE_REG 1034 q1.To.Reg = REG_R27 1035 1036 q2 := obj.Appendp(q1, c.newprog) 1037 q2.Pos = p.Pos 1038 q2.As = ASTP 1039 q2.From.Type = obj.TYPE_REGREG 1040 q2.From.Reg = REGFP 1041 q2.From.Offset = int64(REG_R27) 1042 q2.To.Type = obj.TYPE_MEM 1043 q2.To.Reg = REGSP 1044 q2.To.Offset = -24 1045 1046 // maintain FP for DUFFZERO 1047 q3 := obj.Appendp(q2, c.newprog) 1048 q3.Pos = p.Pos 1049 q3.As = ASUB 1050 q3.From.Type = obj.TYPE_CONST 1051 q3.From.Offset = 24 1052 q3.Reg = REGSP 1053 q3.To.Type = obj.TYPE_REG 1054 q3.To.Reg = REGFP 1055 1056 q5 := obj.Appendp(q4, c.newprog) 1057 q5.Pos = p.Pos 1058 q5.As = ASUB 1059 q5.From.Type = obj.TYPE_CONST 1060 q5.From.Offset = 8 1061 q5.Reg = REGSP 1062 q5.To.Type = obj.TYPE_REG 1063 q5.To.Reg = REGFP 1064 q1.From.SetTarget(q5) 1065 p = q5 1066 } 1067 1068 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 { 1069 f := c.cursym.Func() 1070 if f.FuncFlag&abi.FuncFlagSPWrite == 0 { 1071 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite 1072 if ctxt.Debugvlog || !ctxt.IsAsm { 1073 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p) 1074 if !ctxt.IsAsm { 1075 ctxt.Diag("invalid auto-SPWRITE in non-assembly") 1076 ctxt.DiagFlush() 1077 log.Fatalf("bad SPWRITE") 1078 } 1079 } 1080 } 1081 } 1082 if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) { 1083 offset := p.From.Offset 1084 op := offset & (3 << 22) 1085 if op != SHIFT_LL { 1086 ctxt.Diag("illegal combination: %v", p) 1087 } 1088 r := (offset >> 16) & 31 1089 shift := (offset >> 10) & 63 1090 if shift > 4 { 1091 // the shift amount is out of range, in order to avoid repeated error 1092 // reportings, don't call ctxt.Diag, because asmout case 27 has the 1093 // same check. 1094 shift = 7 1095 } 1096 p.From.Type = obj.TYPE_REG 1097 p.From.Reg = int16(REG_LSL + r + (shift&7)<<5) 1098 p.From.Offset = 0 1099 } 1100 } 1101} 1102 1103func nocache(p *obj.Prog) { 1104 p.Optab = 0 1105 p.From.Class = 0 1106 p.To.Class = 0 1107} 1108 1109var unaryDst = map[obj.As]bool{ 1110 AWORD: true, 1111 ADWORD: true, 1112 ABL: true, 1113 AB: true, 1114 ACLREX: true, 1115} 1116 1117var Linkarm64 = obj.LinkArch{ 1118 Arch: sys.ArchARM64, 1119 Init: buildop, 1120 Preprocess: preprocess, 1121 Assemble: span7, 1122 Progedit: progedit, 1123 UnaryDst: unaryDst, 1124 DWARFRegisters: ARM64DWARFRegisters, 1125} 1126