github.com/zxy12/go_duplicate_1_12@v0.0.0-20200217043740-b1636fc0368b/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 = int64(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) + (int64(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 if isANDWop(p.As) && p.From.Type == obj.TYPE_CONST { 315 v := p.From.Offset & 0xffffffff 316 p.From.Offset = v | v<<32 317 } 318 319 if c.ctxt.Flag_dynlink { 320 c.rewriteToUseGot(p) 321 } 322 } 323 324 // Rewrite p, if necessary, to access global data via the global offset table. 325 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) { 326 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 327 // ADUFFxxx $offset 328 // becomes 329 // MOVD runtime.duffxxx@GOT, REGTMP 330 // ADD $offset, REGTMP 331 // CALL REGTMP 332 var sym *obj.LSym 333 if p.As == obj.ADUFFZERO { 334 sym = c.ctxt.Lookup("runtime.duffzero") 335 } else { 336 sym = c.ctxt.Lookup("runtime.duffcopy") 337 } 338 offset := p.To.Offset 339 p.As = AMOVD 340 p.From.Type = obj.TYPE_MEM 341 p.From.Name = obj.NAME_GOTREF 342 p.From.Sym = sym 343 p.To.Type = obj.TYPE_REG 344 p.To.Reg = REGTMP 345 p.To.Name = obj.NAME_NONE 346 p.To.Offset = 0 347 p.To.Sym = nil 348 p1 := obj.Appendp(p, c.newprog) 349 p1.As = AADD 350 p1.From.Type = obj.TYPE_CONST 351 p1.From.Offset = offset 352 p1.To.Type = obj.TYPE_REG 353 p1.To.Reg = REGTMP 354 p2 := obj.Appendp(p1, c.newprog) 355 p2.As = obj.ACALL 356 p2.To.Type = obj.TYPE_REG 357 p2.To.Reg = REGTMP 358 } 359 360 // We only care about global data: NAME_EXTERN means a global 361 // symbol in the Go sense, and p.Sym.Local is true for a few 362 // internally defined symbols. 363 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 364 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 365 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 366 if p.As != AMOVD { 367 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 368 } 369 if p.To.Type != obj.TYPE_REG { 370 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 371 } 372 p.From.Type = obj.TYPE_MEM 373 p.From.Name = obj.NAME_GOTREF 374 if p.From.Offset != 0 { 375 q := obj.Appendp(p, c.newprog) 376 q.As = AADD 377 q.From.Type = obj.TYPE_CONST 378 q.From.Offset = p.From.Offset 379 q.To = p.To 380 p.From.Offset = 0 381 } 382 } 383 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 384 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 385 } 386 var source *obj.Addr 387 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 388 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP) 389 // An addition may be inserted between the two MOVs if there is an offset. 390 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 391 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 392 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 393 } 394 source = &p.From 395 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 396 source = &p.To 397 } else { 398 return 399 } 400 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 401 return 402 } 403 if source.Sym.Type == objabi.STLSBSS { 404 return 405 } 406 if source.Type != obj.TYPE_MEM { 407 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 408 } 409 p1 := obj.Appendp(p, c.newprog) 410 p2 := obj.Appendp(p1, c.newprog) 411 p1.As = AMOVD 412 p1.From.Type = obj.TYPE_MEM 413 p1.From.Sym = source.Sym 414 p1.From.Name = obj.NAME_GOTREF 415 p1.To.Type = obj.TYPE_REG 416 p1.To.Reg = REGTMP 417 418 p2.As = p.As 419 p2.From = p.From 420 p2.To = p.To 421 if p.From.Name == obj.NAME_EXTERN { 422 p2.From.Reg = REGTMP 423 p2.From.Name = obj.NAME_NONE 424 p2.From.Sym = nil 425 } else if p.To.Name == obj.NAME_EXTERN { 426 p2.To.Reg = REGTMP 427 p2.To.Name = obj.NAME_NONE 428 p2.To.Sym = nil 429 } else { 430 return 431 } 432 obj.Nopout(p) 433 } 434 435 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 436 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { 437 return 438 } 439 440 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym} 441 442 p := c.cursym.Func.Text 443 textstksiz := p.To.Offset 444 if textstksiz == -8 { 445 // Historical way to mark NOFRAME. 446 p.From.Sym.Set(obj.AttrNoFrame, true) 447 textstksiz = 0 448 } 449 if textstksiz < 0 { 450 c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz) 451 } 452 if p.From.Sym.NoFrame() { 453 if textstksiz != 0 { 454 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 455 } 456 } 457 458 c.cursym.Func.Args = p.To.Val.(int32) 459 c.cursym.Func.Locals = int32(textstksiz) 460 461 /* 462 * find leaf subroutines 463 * strip NOPs 464 * expand RET 465 */ 466 q := (*obj.Prog)(nil) 467 var q1 *obj.Prog 468 for p := c.cursym.Func.Text; p != nil; p = p.Link { 469 switch p.As { 470 case obj.ATEXT: 471 p.Mark |= LEAF 472 473 case obj.ARET: 474 break 475 476 case obj.ANOP: 477 if p.Link != nil { 478 q1 = p.Link 479 q.Link = q1 /* q is non-nop */ 480 q1.Mark |= p.Mark 481 } 482 continue 483 484 case ABL, 485 obj.ADUFFZERO, 486 obj.ADUFFCOPY: 487 c.cursym.Func.Text.Mark &^= LEAF 488 fallthrough 489 490 case ACBNZ, 491 ACBZ, 492 ACBNZW, 493 ACBZW, 494 ATBZ, 495 ATBNZ, 496 AB, 497 ABEQ, 498 ABNE, 499 ABCS, 500 ABHS, 501 ABCC, 502 ABLO, 503 ABMI, 504 ABPL, 505 ABVS, 506 ABVC, 507 ABHI, 508 ABLS, 509 ABGE, 510 ABLT, 511 ABGT, 512 ABLE, 513 AADR, /* strange */ 514 AADRP: 515 q1 = p.Pcond 516 517 if q1 != nil { 518 for q1.As == obj.ANOP { 519 q1 = q1.Link 520 p.Pcond = q1 521 } 522 } 523 524 break 525 } 526 527 q = p 528 } 529 530 var retjmp *obj.LSym 531 for p := c.cursym.Func.Text; p != nil; p = p.Link { 532 o := p.As 533 switch o { 534 case obj.ATEXT: 535 c.cursym.Func.Text = p 536 c.autosize = int32(textstksiz) 537 538 if p.Mark&LEAF != 0 && c.autosize == 0 { 539 // A leaf function with no locals has no frame. 540 p.From.Sym.Set(obj.AttrNoFrame, true) 541 } 542 543 if !p.From.Sym.NoFrame() { 544 // If there is a stack frame at all, it includes 545 // space to save the LR. 546 c.autosize += 8 547 } 548 549 if c.autosize != 0 { 550 extrasize := int32(0) 551 if c.autosize%16 == 8 { 552 // Allocate extra 8 bytes on the frame top to save FP 553 extrasize = 8 554 } else if c.autosize&(16-1) == 0 { 555 // Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16 556 extrasize = 16 557 } else { 558 c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8) 559 } 560 c.autosize += extrasize 561 c.cursym.Func.Locals += extrasize 562 563 // low 32 bits for autosize 564 // high 32 bits for extrasize 565 p.To.Offset = int64(c.autosize) | int64(extrasize)<<32 566 } else { 567 // NOFRAME 568 p.To.Offset = 0 569 } 570 571 if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 { 572 if c.ctxt.Debugvlog { 573 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name) 574 } 575 c.cursym.Func.Text.Mark |= LEAF 576 } 577 578 if cursym.Func.Text.Mark&LEAF != 0 { 579 cursym.Set(obj.AttrLeaf, true) 580 if p.From.Sym.NoFrame() { 581 break 582 } 583 } 584 585 if !p.From.Sym.NoSplit() { 586 p = c.stacksplit(p, c.autosize) // emit split check 587 } 588 589 aoffset := c.autosize 590 if aoffset > 0xF0 { 591 aoffset = 0xF0 592 } 593 594 // Frame is non-empty. Make sure to save link register, even if 595 // it is a leaf function, so that traceback works. 596 q = p 597 if c.autosize > aoffset { 598 // Frame size is too large for a MOVD.W instruction. 599 // Store link register before decrementing SP, so if a signal comes 600 // during the execution of the function prologue, the traceback 601 // code will not see a half-updated stack frame. 602 q = obj.Appendp(q, c.newprog) 603 q.Pos = p.Pos 604 q.As = ASUB 605 q.From.Type = obj.TYPE_CONST 606 q.From.Offset = int64(c.autosize) 607 q.Reg = REGSP 608 q.To.Type = obj.TYPE_REG 609 q.To.Reg = REGTMP 610 611 q = obj.Appendp(q, c.newprog) 612 q.Pos = p.Pos 613 q.As = AMOVD 614 q.From.Type = obj.TYPE_REG 615 q.From.Reg = REGLINK 616 q.To.Type = obj.TYPE_MEM 617 q.To.Reg = REGTMP 618 619 q1 = obj.Appendp(q, c.newprog) 620 q1.Pos = p.Pos 621 q1.As = AMOVD 622 q1.From.Type = obj.TYPE_REG 623 q1.From.Reg = REGTMP 624 q1.To.Type = obj.TYPE_REG 625 q1.To.Reg = REGSP 626 q1.Spadj = c.autosize 627 } else { 628 // small frame, update SP and save LR in a single MOVD.W instruction 629 q1 = obj.Appendp(q, c.newprog) 630 q1.As = AMOVD 631 q1.Pos = p.Pos 632 q1.From.Type = obj.TYPE_REG 633 q1.From.Reg = REGLINK 634 q1.To.Type = obj.TYPE_MEM 635 q1.Scond = C_XPRE 636 q1.To.Offset = int64(-aoffset) 637 q1.To.Reg = REGSP 638 q1.Spadj = aoffset 639 } 640 641 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { 642 q1 = obj.Appendp(q1, c.newprog) 643 q1.Pos = p.Pos 644 q1.As = AMOVD 645 q1.From.Type = obj.TYPE_REG 646 q1.From.Reg = REGFP 647 q1.To.Type = obj.TYPE_MEM 648 q1.To.Reg = REGSP 649 q1.To.Offset = -8 650 651 q1 = obj.Appendp(q1, c.newprog) 652 q1.Pos = p.Pos 653 q1.As = ASUB 654 q1.From.Type = obj.TYPE_CONST 655 q1.From.Offset = 8 656 q1.Reg = REGSP 657 q1.To.Type = obj.TYPE_REG 658 q1.To.Reg = REGFP 659 } 660 661 if c.cursym.Func.Text.From.Sym.Wrapper() { 662 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 663 // 664 // MOV g_panic(g), R1 665 // CBNZ checkargp 666 // end: 667 // NOP 668 // ... function body ... 669 // checkargp: 670 // MOV panic_argp(R1), R2 671 // ADD $(autosize+8), RSP, R3 672 // CMP R2, R3 673 // BNE end 674 // ADD $8, RSP, R4 675 // MOVD R4, panic_argp(R1) 676 // B end 677 // 678 // The NOP is needed to give the jumps somewhere to land. 679 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes. 680 q = q1 681 682 // MOV g_panic(g), R1 683 q = obj.Appendp(q, c.newprog) 684 q.As = AMOVD 685 q.From.Type = obj.TYPE_MEM 686 q.From.Reg = REGG 687 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 688 q.To.Type = obj.TYPE_REG 689 q.To.Reg = REG_R1 690 691 // CBNZ R1, checkargp 692 cbnz := obj.Appendp(q, c.newprog) 693 cbnz.As = ACBNZ 694 cbnz.From.Type = obj.TYPE_REG 695 cbnz.From.Reg = REG_R1 696 cbnz.To.Type = obj.TYPE_BRANCH 697 698 // Empty branch target at the top of the function body 699 end := obj.Appendp(cbnz, c.newprog) 700 end.As = obj.ANOP 701 702 // find the end of the function 703 var last *obj.Prog 704 for last = end; last.Link != nil; last = last.Link { 705 } 706 707 // MOV panic_argp(R1), R2 708 mov := obj.Appendp(last, c.newprog) 709 mov.As = AMOVD 710 mov.From.Type = obj.TYPE_MEM 711 mov.From.Reg = REG_R1 712 mov.From.Offset = 0 // Panic.argp 713 mov.To.Type = obj.TYPE_REG 714 mov.To.Reg = REG_R2 715 716 // CBNZ branches to the MOV above 717 cbnz.Pcond = mov 718 719 // ADD $(autosize+8), SP, R3 720 q = obj.Appendp(mov, c.newprog) 721 q.As = AADD 722 q.From.Type = obj.TYPE_CONST 723 q.From.Offset = int64(c.autosize) + 8 724 q.Reg = REGSP 725 q.To.Type = obj.TYPE_REG 726 q.To.Reg = REG_R3 727 728 // CMP R2, R3 729 q = obj.Appendp(q, c.newprog) 730 q.As = ACMP 731 q.From.Type = obj.TYPE_REG 732 q.From.Reg = REG_R2 733 q.Reg = REG_R3 734 735 // BNE end 736 q = obj.Appendp(q, c.newprog) 737 q.As = ABNE 738 q.To.Type = obj.TYPE_BRANCH 739 q.Pcond = end 740 741 // ADD $8, SP, R4 742 q = obj.Appendp(q, c.newprog) 743 q.As = AADD 744 q.From.Type = obj.TYPE_CONST 745 q.From.Offset = 8 746 q.Reg = REGSP 747 q.To.Type = obj.TYPE_REG 748 q.To.Reg = REG_R4 749 750 // MOV R4, panic_argp(R1) 751 q = obj.Appendp(q, c.newprog) 752 q.As = AMOVD 753 q.From.Type = obj.TYPE_REG 754 q.From.Reg = REG_R4 755 q.To.Type = obj.TYPE_MEM 756 q.To.Reg = REG_R1 757 q.To.Offset = 0 // Panic.argp 758 759 // B end 760 q = obj.Appendp(q, c.newprog) 761 q.As = AB 762 q.To.Type = obj.TYPE_BRANCH 763 q.Pcond = end 764 } 765 766 case obj.ARET: 767 nocache(p) 768 if p.From.Type == obj.TYPE_CONST { 769 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 770 break 771 } 772 773 retjmp = p.To.Sym 774 p.To = obj.Addr{} 775 if c.cursym.Func.Text.Mark&LEAF != 0 { 776 if c.autosize != 0 { 777 p.As = AADD 778 p.From.Type = obj.TYPE_CONST 779 p.From.Offset = int64(c.autosize) 780 p.To.Type = obj.TYPE_REG 781 p.To.Reg = REGSP 782 p.Spadj = -c.autosize 783 784 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { 785 p = obj.Appendp(p, c.newprog) 786 p.As = ASUB 787 p.From.Type = obj.TYPE_CONST 788 p.From.Offset = 8 789 p.Reg = REGSP 790 p.To.Type = obj.TYPE_REG 791 p.To.Reg = REGFP 792 } 793 } 794 } else { 795 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ 796 797 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { 798 p.As = AMOVD 799 p.From.Type = obj.TYPE_MEM 800 p.From.Reg = REGSP 801 p.From.Offset = -8 802 p.To.Type = obj.TYPE_REG 803 p.To.Reg = REGFP 804 p = obj.Appendp(p, c.newprog) 805 } 806 807 aoffset := c.autosize 808 809 if aoffset > 0xF0 { 810 aoffset = 0xF0 811 } 812 p.As = AMOVD 813 p.From.Type = obj.TYPE_MEM 814 p.Scond = C_XPOST 815 p.From.Offset = int64(aoffset) 816 p.From.Reg = REGSP 817 p.To.Type = obj.TYPE_REG 818 p.To.Reg = REGLINK 819 p.Spadj = -aoffset 820 if c.autosize > aoffset { 821 q = newprog() 822 q.As = AADD 823 q.From.Type = obj.TYPE_CONST 824 q.From.Offset = int64(c.autosize) - int64(aoffset) 825 q.To.Type = obj.TYPE_REG 826 q.To.Reg = REGSP 827 q.Link = p.Link 828 q.Spadj = int32(-q.From.Offset) 829 q.Pos = p.Pos 830 p.Link = q 831 p = q 832 } 833 } 834 835 if p.As != obj.ARET { 836 q = newprog() 837 q.Pos = p.Pos 838 q.Link = p.Link 839 p.Link = q 840 p = q 841 } 842 843 if retjmp != nil { // retjmp 844 p.As = AB 845 p.To.Type = obj.TYPE_BRANCH 846 p.To.Sym = retjmp 847 p.Spadj = +c.autosize 848 break 849 } 850 851 p.As = obj.ARET 852 p.To.Type = obj.TYPE_MEM 853 p.To.Offset = 0 854 p.To.Reg = REGLINK 855 p.Spadj = +c.autosize 856 857 case AADD, ASUB: 858 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 859 if p.As == AADD { 860 p.Spadj = int32(-p.From.Offset) 861 } else { 862 p.Spadj = int32(+p.From.Offset) 863 } 864 } 865 866 case obj.AGETCALLERPC: 867 if cursym.Leaf() { 868 /* MOVD LR, Rd */ 869 p.As = AMOVD 870 p.From.Type = obj.TYPE_REG 871 p.From.Reg = REGLINK 872 } else { 873 /* MOVD (RSP), Rd */ 874 p.As = AMOVD 875 p.From.Type = obj.TYPE_MEM 876 p.From.Reg = REGSP 877 } 878 879 case obj.ADUFFCOPY: 880 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { 881 // ADR ret_addr, R27 882 // STP (FP, R27), -24(SP) 883 // SUB 24, SP, FP 884 // DUFFCOPY 885 // ret_addr: 886 // SUB 8, SP, FP 887 888 q1 := p 889 // copy DUFFCOPY from q1 to q4 890 q4 := obj.Appendp(p, c.newprog) 891 q4.Pos = p.Pos 892 q4.As = obj.ADUFFCOPY 893 q4.To = p.To 894 895 q1.As = AADR 896 q1.From.Type = obj.TYPE_BRANCH 897 q1.To.Type = obj.TYPE_REG 898 q1.To.Reg = REG_R27 899 900 q2 := obj.Appendp(q1, c.newprog) 901 q2.Pos = p.Pos 902 q2.As = ASTP 903 q2.From.Type = obj.TYPE_REGREG 904 q2.From.Reg = REGFP 905 q2.From.Offset = int64(REG_R27) 906 q2.To.Type = obj.TYPE_MEM 907 q2.To.Reg = REGSP 908 q2.To.Offset = -24 909 910 // maintaine FP for DUFFCOPY 911 q3 := obj.Appendp(q2, c.newprog) 912 q3.Pos = p.Pos 913 q3.As = ASUB 914 q3.From.Type = obj.TYPE_CONST 915 q3.From.Offset = 24 916 q3.Reg = REGSP 917 q3.To.Type = obj.TYPE_REG 918 q3.To.Reg = REGFP 919 920 q5 := obj.Appendp(q4, c.newprog) 921 q5.Pos = p.Pos 922 q5.As = ASUB 923 q5.From.Type = obj.TYPE_CONST 924 q5.From.Offset = 8 925 q5.Reg = REGSP 926 q5.To.Type = obj.TYPE_REG 927 q5.To.Reg = REGFP 928 q1.Pcond = q5 929 p = q5 930 } 931 932 case obj.ADUFFZERO: 933 if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) { 934 // ADR ret_addr, R27 935 // STP (FP, R27), -24(SP) 936 // SUB 24, SP, FP 937 // DUFFZERO 938 // ret_addr: 939 // SUB 8, SP, FP 940 941 q1 := p 942 // copy DUFFZERO from q1 to q4 943 q4 := obj.Appendp(p, c.newprog) 944 q4.Pos = p.Pos 945 q4.As = obj.ADUFFZERO 946 q4.To = p.To 947 948 q1.As = AADR 949 q1.From.Type = obj.TYPE_BRANCH 950 q1.To.Type = obj.TYPE_REG 951 q1.To.Reg = REG_R27 952 953 q2 := obj.Appendp(q1, c.newprog) 954 q2.Pos = p.Pos 955 q2.As = ASTP 956 q2.From.Type = obj.TYPE_REGREG 957 q2.From.Reg = REGFP 958 q2.From.Offset = int64(REG_R27) 959 q2.To.Type = obj.TYPE_MEM 960 q2.To.Reg = REGSP 961 q2.To.Offset = -24 962 963 // maintaine FP for DUFFZERO 964 q3 := obj.Appendp(q2, c.newprog) 965 q3.Pos = p.Pos 966 q3.As = ASUB 967 q3.From.Type = obj.TYPE_CONST 968 q3.From.Offset = 24 969 q3.Reg = REGSP 970 q3.To.Type = obj.TYPE_REG 971 q3.To.Reg = REGFP 972 973 q5 := obj.Appendp(q4, c.newprog) 974 q5.Pos = p.Pos 975 q5.As = ASUB 976 q5.From.Type = obj.TYPE_CONST 977 q5.From.Offset = 8 978 q5.Reg = REGSP 979 q5.To.Type = obj.TYPE_REG 980 q5.To.Reg = REGFP 981 q1.Pcond = q5 982 p = q5 983 } 984 } 985 } 986 } 987 988 func nocache(p *obj.Prog) { 989 p.Optab = 0 990 p.From.Class = 0 991 p.To.Class = 0 992 } 993 994 var unaryDst = map[obj.As]bool{ 995 AWORD: true, 996 ADWORD: true, 997 ABL: true, 998 AB: true, 999 ACLREX: true, 1000 } 1001 1002 var Linkarm64 = obj.LinkArch{ 1003 Arch: sys.ArchARM64, 1004 Init: buildop, 1005 Preprocess: preprocess, 1006 Assemble: span7, 1007 Progedit: progedit, 1008 UnaryDst: unaryDst, 1009 DWARFRegisters: ARM64DWARFRegisters, 1010 }