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