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