github.com/rakyll/go@v0.0.0-20170216000551-64c02460d703/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/sys" 36 "fmt" 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 stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 52 // MOV g_stackguard(g), R1 53 p = obj.Appendp(ctxt, p) 54 55 p.As = AMOVD 56 p.From.Type = obj.TYPE_MEM 57 p.From.Reg = REGG 58 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 59 if ctxt.Cursym.CFunc() { 60 p.From.Offset = 3 * int64(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 <= obj.StackSmall { 67 // small stack: SP < stackguard 68 // MOV SP, R2 69 // CMP stackguard, R2 70 p = obj.Appendp(ctxt, p) 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(ctxt, p) 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 <= obj.StackBig { 84 // large stack: SP-framesize < stackguard-StackSmall 85 // SUB $framesize, SP, R2 86 // CMP stackguard, R2 87 p = obj.Appendp(ctxt, p) 88 89 p.As = ASUB 90 p.From.Type = obj.TYPE_CONST 91 p.From.Offset = int64(framesize) 92 p.Reg = REGSP 93 p.To.Type = obj.TYPE_REG 94 p.To.Reg = REG_R2 95 96 p = obj.Appendp(ctxt, p) 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(ctxt, p) 114 115 p.As = ACMP 116 p.From.Type = obj.TYPE_CONST 117 p.From.Offset = obj.StackPreempt 118 p.Reg = REG_R1 119 120 p = obj.Appendp(ctxt, p) 121 q = p 122 p.As = ABEQ 123 p.To.Type = obj.TYPE_BRANCH 124 125 p = obj.Appendp(ctxt, p) 126 p.As = AADD 127 p.From.Type = obj.TYPE_CONST 128 p.From.Offset = obj.StackGuard 129 p.Reg = REGSP 130 p.To.Type = obj.TYPE_REG 131 p.To.Reg = REG_R2 132 133 p = obj.Appendp(ctxt, p) 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(ctxt, p) 141 p.As = AMOVD 142 p.From.Type = obj.TYPE_CONST 143 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 144 p.To.Type = obj.TYPE_REG 145 p.To.Reg = REG_R3 146 147 p = obj.Appendp(ctxt, p) 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(ctxt, p) 156 bls.As = ABLS 157 bls.To.Type = obj.TYPE_BRANCH 158 159 var last *obj.Prog 160 for last = ctxt.Cursym.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(ctxt, last) 167 spfix.As = obj.ANOP 168 spfix.Spadj = -framesize 169 170 pcdata := obj.Appendp(ctxt, spfix) 171 pcdata.Pos = ctxt.Cursym.Text.Pos 172 pcdata.Mode = ctxt.Cursym.Text.Mode 173 pcdata.As = obj.APCDATA 174 pcdata.From.Type = obj.TYPE_CONST 175 pcdata.From.Offset = obj.PCDATA_StackMapIndex 176 pcdata.To.Type = obj.TYPE_CONST 177 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 178 179 // MOV LR, R3 180 movlr := obj.Appendp(ctxt, pcdata) 181 movlr.As = AMOVD 182 movlr.From.Type = obj.TYPE_REG 183 movlr.From.Reg = REGLINK 184 movlr.To.Type = obj.TYPE_REG 185 movlr.To.Reg = REG_R3 186 if q != nil { 187 q.Pcond = movlr 188 } 189 bls.Pcond = movlr 190 191 debug := movlr 192 if false { 193 debug = obj.Appendp(ctxt, debug) 194 debug.As = AMOVD 195 debug.From.Type = obj.TYPE_CONST 196 debug.From.Offset = int64(framesize) 197 debug.To.Type = obj.TYPE_REG 198 debug.To.Reg = REGTMP 199 } 200 201 // BL runtime.morestack(SB) 202 call := obj.Appendp(ctxt, debug) 203 call.As = ABL 204 call.To.Type = obj.TYPE_BRANCH 205 morestack := "runtime.morestack" 206 switch { 207 case ctxt.Cursym.CFunc(): 208 morestack = "runtime.morestackc" 209 case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0: 210 morestack = "runtime.morestack_noctxt" 211 } 212 call.To.Sym = obj.Linklookup(ctxt, morestack, 0) 213 214 // B start 215 jmp := obj.Appendp(ctxt, call) 216 jmp.As = AB 217 jmp.To.Type = obj.TYPE_BRANCH 218 jmp.Pcond = ctxt.Cursym.Text.Link 219 jmp.Spadj = +framesize 220 221 // placeholder for bls's jump target 222 // p = obj.Appendp(ctxt, p) 223 // p.As = obj.ANOP 224 225 return bls 226 } 227 228 func progedit(ctxt *obj.Link, p *obj.Prog) { 229 p.From.Class = 0 230 p.To.Class = 0 231 232 // $0 results in C_ZCON, which matches both C_REG and various 233 // C_xCON, however the C_REG cases in asmout don't expect a 234 // constant, so they will use the register fields and assemble 235 // a R0. To prevent that, rewrite $0 as ZR. 236 if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 { 237 p.From.Type = obj.TYPE_REG 238 p.From.Reg = REGZERO 239 } 240 if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 { 241 p.To.Type = obj.TYPE_REG 242 p.To.Reg = REGZERO 243 } 244 245 // Rewrite BR/BL to symbol as TYPE_BRANCH. 246 switch p.As { 247 case AB, 248 ABL, 249 obj.ARET, 250 obj.ADUFFZERO, 251 obj.ADUFFCOPY: 252 if p.To.Sym != nil { 253 p.To.Type = obj.TYPE_BRANCH 254 } 255 break 256 } 257 258 // Rewrite float constants to values stored in memory. 259 switch p.As { 260 case AFMOVS: 261 if p.From.Type == obj.TYPE_FCONST { 262 f32 := float32(p.From.Val.(float64)) 263 i32 := math.Float32bits(f32) 264 if i32 == 0 { 265 p.From.Type = obj.TYPE_REG 266 p.From.Reg = REGZERO 267 break 268 } 269 literal := fmt.Sprintf("$f32.%08x", i32) 270 s := obj.Linklookup(ctxt, literal, 0) 271 s.Size = 4 272 p.From.Type = obj.TYPE_MEM 273 p.From.Sym = s 274 p.From.Sym.Set(obj.AttrLocal, true) 275 p.From.Name = obj.NAME_EXTERN 276 p.From.Offset = 0 277 } 278 279 case AFMOVD: 280 if p.From.Type == obj.TYPE_FCONST { 281 i64 := math.Float64bits(p.From.Val.(float64)) 282 if i64 == 0 { 283 p.From.Type = obj.TYPE_REG 284 p.From.Reg = REGZERO 285 break 286 } 287 literal := fmt.Sprintf("$f64.%016x", i64) 288 s := obj.Linklookup(ctxt, literal, 0) 289 s.Size = 8 290 p.From.Type = obj.TYPE_MEM 291 p.From.Sym = s 292 p.From.Sym.Set(obj.AttrLocal, true) 293 p.From.Name = obj.NAME_EXTERN 294 p.From.Offset = 0 295 } 296 297 break 298 } 299 300 // Rewrite negative immediates as positive immediates with 301 // complementary instruction. 302 switch p.As { 303 case AADD, ASUB, ACMP, ACMN: 304 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 { 305 p.From.Offset = -p.From.Offset 306 p.As = complements[p.As] 307 } 308 case AADDW, ASUBW, ACMPW, ACMNW: 309 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 { 310 p.From.Offset = -p.From.Offset 311 p.As = complements[p.As] 312 } 313 } 314 315 // For 32-bit logical instruction with constant, 316 // rewrite the high 32-bit to be a repetition of 317 // the low 32-bit, so that the BITCON test can be 318 // shared for both 32-bit and 64-bit. 32-bit ops 319 // will zero the high 32-bit of the destination 320 // register anyway. 321 switch p.As { 322 case AANDW, AORRW, AEORW, AANDSW: 323 if p.From.Type == obj.TYPE_CONST { 324 v := p.From.Offset & 0xffffffff 325 p.From.Offset = v | v<<32 326 } 327 } 328 329 if ctxt.Flag_dynlink { 330 rewriteToUseGot(ctxt, p) 331 } 332 } 333 334 // Rewrite p, if necessary, to access global data via the global offset table. 335 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) { 336 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 337 // ADUFFxxx $offset 338 // becomes 339 // MOVD runtime.duffxxx@GOT, REGTMP 340 // ADD $offset, REGTMP 341 // CALL REGTMP 342 var sym *obj.LSym 343 if p.As == obj.ADUFFZERO { 344 sym = obj.Linklookup(ctxt, "runtime.duffzero", 0) 345 } else { 346 sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0) 347 } 348 offset := p.To.Offset 349 p.As = AMOVD 350 p.From.Type = obj.TYPE_MEM 351 p.From.Name = obj.NAME_GOTREF 352 p.From.Sym = sym 353 p.To.Type = obj.TYPE_REG 354 p.To.Reg = REGTMP 355 p.To.Name = obj.NAME_NONE 356 p.To.Offset = 0 357 p.To.Sym = nil 358 p1 := obj.Appendp(ctxt, p) 359 p1.As = AADD 360 p1.From.Type = obj.TYPE_CONST 361 p1.From.Offset = offset 362 p1.To.Type = obj.TYPE_REG 363 p1.To.Reg = REGTMP 364 p2 := obj.Appendp(ctxt, p1) 365 p2.As = obj.ACALL 366 p2.To.Type = obj.TYPE_REG 367 p2.To.Reg = REGTMP 368 } 369 370 // We only care about global data: NAME_EXTERN means a global 371 // symbol in the Go sense, and p.Sym.Local is true for a few 372 // internally defined symbols. 373 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 374 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 375 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 376 if p.As != AMOVD { 377 ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 378 } 379 if p.To.Type != obj.TYPE_REG { 380 ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 381 } 382 p.From.Type = obj.TYPE_MEM 383 p.From.Name = obj.NAME_GOTREF 384 if p.From.Offset != 0 { 385 q := obj.Appendp(ctxt, p) 386 q.As = AADD 387 q.From.Type = obj.TYPE_CONST 388 q.From.Offset = p.From.Offset 389 q.To = p.To 390 p.From.Offset = 0 391 } 392 } 393 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 394 ctxt.Diag("don't know how to handle %v with -dynlink", p) 395 } 396 var source *obj.Addr 397 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 398 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP) 399 // An addition may be inserted between the two MOVs if there is an offset. 400 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 401 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 402 ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 403 } 404 source = &p.From 405 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 406 source = &p.To 407 } else { 408 return 409 } 410 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 411 return 412 } 413 if source.Sym.Type == obj.STLSBSS { 414 return 415 } 416 if source.Type != obj.TYPE_MEM { 417 ctxt.Diag("don't know how to handle %v with -dynlink", p) 418 } 419 p1 := obj.Appendp(ctxt, p) 420 p2 := obj.Appendp(ctxt, p1) 421 p1.As = AMOVD 422 p1.From.Type = obj.TYPE_MEM 423 p1.From.Sym = source.Sym 424 p1.From.Name = obj.NAME_GOTREF 425 p1.To.Type = obj.TYPE_REG 426 p1.To.Reg = REGTMP 427 428 p2.As = p.As 429 p2.From = p.From 430 p2.To = p.To 431 if p.From.Name == obj.NAME_EXTERN { 432 p2.From.Reg = REGTMP 433 p2.From.Name = obj.NAME_NONE 434 p2.From.Sym = nil 435 } else if p.To.Name == obj.NAME_EXTERN { 436 p2.To.Reg = REGTMP 437 p2.To.Name = obj.NAME_NONE 438 p2.To.Sym = nil 439 } else { 440 return 441 } 442 obj.Nopout(p) 443 } 444 445 func preprocess(ctxt *obj.Link, cursym *obj.LSym) { 446 ctxt.Cursym = cursym 447 448 if cursym.Text == nil || cursym.Text.Link == nil { 449 return 450 } 451 452 p := cursym.Text 453 textstksiz := p.To.Offset 454 aoffset := int32(textstksiz) 455 456 cursym.Args = p.To.Val.(int32) 457 cursym.Locals = int32(textstksiz) 458 459 /* 460 * find leaf subroutines 461 * strip NOPs 462 * expand RET 463 */ 464 q := (*obj.Prog)(nil) 465 var q1 *obj.Prog 466 for p := cursym.Text; p != nil; p = p.Link { 467 switch p.As { 468 case obj.ATEXT: 469 p.Mark |= LEAF 470 471 case obj.ARET: 472 break 473 474 case obj.ANOP: 475 q1 = p.Link 476 q.Link = q1 /* q is non-nop */ 477 q1.Mark |= p.Mark 478 continue 479 480 case ABL, 481 obj.ADUFFZERO, 482 obj.ADUFFCOPY: 483 cursym.Text.Mark &^= LEAF 484 fallthrough 485 486 case ACBNZ, 487 ACBZ, 488 ACBNZW, 489 ACBZW, 490 ATBZ, 491 ATBNZ, 492 AB, 493 ABEQ, 494 ABNE, 495 ABCS, 496 ABHS, 497 ABCC, 498 ABLO, 499 ABMI, 500 ABPL, 501 ABVS, 502 ABVC, 503 ABHI, 504 ABLS, 505 ABGE, 506 ABLT, 507 ABGT, 508 ABLE, 509 AADR, /* strange */ 510 AADRP: 511 q1 = p.Pcond 512 513 if q1 != nil { 514 for q1.As == obj.ANOP { 515 q1 = q1.Link 516 p.Pcond = q1 517 } 518 } 519 520 break 521 } 522 523 q = p 524 } 525 526 var q2 *obj.Prog 527 var retjmp *obj.LSym 528 for p := cursym.Text; p != nil; p = p.Link { 529 o := p.As 530 switch o { 531 case obj.ATEXT: 532 cursym.Text = p 533 if textstksiz < 0 { 534 ctxt.Autosize = 0 535 } else { 536 ctxt.Autosize = int32(textstksiz + 8) 537 } 538 if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 { 539 ctxt.Autosize = 0 540 } else if ctxt.Autosize&(16-1) != 0 { 541 // The frame includes an LR. 542 // If the frame size is 8, it's only an LR, 543 // so there's no potential for breaking references to 544 // local variables by growing the frame size, 545 // because there are no local variables. 546 // But otherwise, if there is a non-empty locals section, 547 // the author of the code is responsible for making sure 548 // that the frame size is 8 mod 16. 549 if ctxt.Autosize == 8 { 550 ctxt.Autosize += 8 551 cursym.Locals += 8 552 } else { 553 ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8) 554 } 555 } 556 p.To.Offset = int64(ctxt.Autosize) - 8 557 if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) { 558 if ctxt.Debugvlog != 0 { 559 ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name) 560 } 561 cursym.Text.Mark |= LEAF 562 } 563 564 if !(p.From3.Offset&obj.NOSPLIT != 0) { 565 p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check 566 } 567 568 aoffset = ctxt.Autosize 569 if aoffset > 0xF0 { 570 aoffset = 0xF0 571 } 572 if cursym.Text.Mark&LEAF != 0 { 573 cursym.Set(obj.AttrLeaf, true) 574 if ctxt.Autosize == 0 { 575 break 576 } 577 } 578 579 // Frame is non-empty. Make sure to save link register, even if 580 // it is a leaf function, so that traceback works. 581 q = p 582 if ctxt.Autosize > aoffset { 583 // Frame size is too large for a MOVD.W instruction. 584 // Store link register before decrementing SP, so if a signal comes 585 // during the execution of the function prologue, the traceback 586 // code will not see a half-updated stack frame. 587 q = obj.Appendp(ctxt, q) 588 q.Pos = p.Pos 589 q.As = ASUB 590 q.From.Type = obj.TYPE_CONST 591 q.From.Offset = int64(ctxt.Autosize) 592 q.Reg = REGSP 593 q.To.Type = obj.TYPE_REG 594 q.To.Reg = REGTMP 595 596 q = obj.Appendp(ctxt, q) 597 q.Pos = p.Pos 598 q.As = AMOVD 599 q.From.Type = obj.TYPE_REG 600 q.From.Reg = REGLINK 601 q.To.Type = obj.TYPE_MEM 602 q.To.Reg = REGTMP 603 604 q1 = obj.Appendp(ctxt, q) 605 q1.Pos = p.Pos 606 q1.As = AMOVD 607 q1.From.Type = obj.TYPE_REG 608 q1.From.Reg = REGTMP 609 q1.To.Type = obj.TYPE_REG 610 q1.To.Reg = REGSP 611 q1.Spadj = ctxt.Autosize 612 } else { 613 // small frame, update SP and save LR in a single MOVD.W instruction 614 q1 = obj.Appendp(ctxt, q) 615 q1.As = AMOVD 616 q1.Pos = p.Pos 617 q1.From.Type = obj.TYPE_REG 618 q1.From.Reg = REGLINK 619 q1.To.Type = obj.TYPE_MEM 620 q1.Scond = C_XPRE 621 q1.To.Offset = int64(-aoffset) 622 q1.To.Reg = REGSP 623 q1.Spadj = aoffset 624 } 625 626 if cursym.Text.From3.Offset&obj.WRAPPER != 0 { 627 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 628 // 629 // MOV g_panic(g), R1 630 // CMP ZR, R1 631 // BEQ end 632 // MOV panic_argp(R1), R2 633 // ADD $(autosize+8), RSP, R3 634 // CMP R2, R3 635 // BNE end 636 // ADD $8, RSP, R4 637 // MOVD R4, panic_argp(R1) 638 // end: 639 // NOP 640 // 641 // The NOP is needed to give the jumps somewhere to land. 642 // It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes. 643 q = q1 644 645 q = obj.Appendp(ctxt, q) 646 q.As = AMOVD 647 q.From.Type = obj.TYPE_MEM 648 q.From.Reg = REGG 649 q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 650 q.To.Type = obj.TYPE_REG 651 q.To.Reg = REG_R1 652 653 q = obj.Appendp(ctxt, q) 654 q.As = ACMP 655 q.From.Type = obj.TYPE_REG 656 q.From.Reg = REGZERO 657 q.Reg = REG_R1 658 659 q = obj.Appendp(ctxt, q) 660 q.As = ABEQ 661 q.To.Type = obj.TYPE_BRANCH 662 q1 = q 663 664 q = obj.Appendp(ctxt, q) 665 q.As = AMOVD 666 q.From.Type = obj.TYPE_MEM 667 q.From.Reg = REG_R1 668 q.From.Offset = 0 // Panic.argp 669 q.To.Type = obj.TYPE_REG 670 q.To.Reg = REG_R2 671 672 q = obj.Appendp(ctxt, q) 673 q.As = AADD 674 q.From.Type = obj.TYPE_CONST 675 q.From.Offset = int64(ctxt.Autosize) + 8 676 q.Reg = REGSP 677 q.To.Type = obj.TYPE_REG 678 q.To.Reg = REG_R3 679 680 q = obj.Appendp(ctxt, q) 681 q.As = ACMP 682 q.From.Type = obj.TYPE_REG 683 q.From.Reg = REG_R2 684 q.Reg = REG_R3 685 686 q = obj.Appendp(ctxt, q) 687 q.As = ABNE 688 q.To.Type = obj.TYPE_BRANCH 689 q2 = q 690 691 q = obj.Appendp(ctxt, q) 692 q.As = AADD 693 q.From.Type = obj.TYPE_CONST 694 q.From.Offset = 8 695 q.Reg = REGSP 696 q.To.Type = obj.TYPE_REG 697 q.To.Reg = REG_R4 698 699 q = obj.Appendp(ctxt, q) 700 q.As = AMOVD 701 q.From.Type = obj.TYPE_REG 702 q.From.Reg = REG_R4 703 q.To.Type = obj.TYPE_MEM 704 q.To.Reg = REG_R1 705 q.To.Offset = 0 // Panic.argp 706 707 q = obj.Appendp(ctxt, q) 708 709 q.As = obj.ANOP 710 q1.Pcond = q 711 q2.Pcond = q 712 } 713 714 case obj.ARET: 715 nocache(p) 716 if p.From.Type == obj.TYPE_CONST { 717 ctxt.Diag("using BECOME (%v) is not supported!", p) 718 break 719 } 720 721 retjmp = p.To.Sym 722 p.To = obj.Addr{} 723 if cursym.Text.Mark&LEAF != 0 { 724 if ctxt.Autosize != 0 { 725 p.As = AADD 726 p.From.Type = obj.TYPE_CONST 727 p.From.Offset = int64(ctxt.Autosize) 728 p.To.Type = obj.TYPE_REG 729 p.To.Reg = REGSP 730 p.Spadj = -ctxt.Autosize 731 } 732 } else { 733 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ 734 aoffset = ctxt.Autosize 735 736 if aoffset > 0xF0 { 737 aoffset = 0xF0 738 } 739 p.As = AMOVD 740 p.From.Type = obj.TYPE_MEM 741 p.Scond = C_XPOST 742 p.From.Offset = int64(aoffset) 743 p.From.Reg = REGSP 744 p.To.Type = obj.TYPE_REG 745 p.To.Reg = REGLINK 746 p.Spadj = -aoffset 747 if ctxt.Autosize > aoffset { 748 q = ctxt.NewProg() 749 q.As = AADD 750 q.From.Type = obj.TYPE_CONST 751 q.From.Offset = int64(ctxt.Autosize) - int64(aoffset) 752 q.To.Type = obj.TYPE_REG 753 q.To.Reg = REGSP 754 q.Link = p.Link 755 q.Spadj = int32(-q.From.Offset) 756 q.Pos = p.Pos 757 p.Link = q 758 p = q 759 } 760 } 761 762 if p.As != obj.ARET { 763 q = ctxt.NewProg() 764 q.Pos = p.Pos 765 q.Link = p.Link 766 p.Link = q 767 p = q 768 } 769 770 if retjmp != nil { // retjmp 771 p.As = AB 772 p.To.Type = obj.TYPE_BRANCH 773 p.To.Sym = retjmp 774 p.Spadj = +ctxt.Autosize 775 break 776 } 777 778 p.As = obj.ARET 779 p.To.Type = obj.TYPE_MEM 780 p.To.Offset = 0 781 p.To.Reg = REGLINK 782 p.Spadj = +ctxt.Autosize 783 784 case AADD, ASUB: 785 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 786 if p.As == AADD { 787 p.Spadj = int32(-p.From.Offset) 788 } else { 789 p.Spadj = int32(+p.From.Offset) 790 } 791 } 792 break 793 } 794 } 795 } 796 797 func nocache(p *obj.Prog) { 798 p.Optab = 0 799 p.From.Class = 0 800 p.To.Class = 0 801 } 802 803 var unaryDst = map[obj.As]bool{ 804 AWORD: true, 805 ADWORD: true, 806 ABL: true, 807 AB: true, 808 ASVC: true, 809 } 810 811 var Linkarm64 = obj.LinkArch{ 812 Arch: sys.ArchARM64, 813 Preprocess: preprocess, 814 Assemble: span7, 815 Progedit: progedit, 816 UnaryDst: unaryDst, 817 }