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