github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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.GetFrom3() != nil && p.GetFrom3().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 retjmp *obj.LSym 519 for p := c.cursym.Func.Text; p != nil; p = p.Link { 520 o := p.As 521 switch o { 522 case obj.ATEXT: 523 c.cursym.Func.Text = p 524 if textstksiz < 0 { 525 c.autosize = 0 526 } else { 527 c.autosize = int32(textstksiz + 8) 528 } 529 if (c.cursym.Func.Text.Mark&LEAF != 0) && c.autosize <= 8 { 530 c.autosize = 0 531 } else if c.autosize&(16-1) != 0 { 532 // The frame includes an LR. 533 // If the frame size is 8, it's only an LR, 534 // so there's no potential for breaking references to 535 // local variables by growing the frame size, 536 // because there are no local variables. 537 // But otherwise, if there is a non-empty locals section, 538 // the author of the code is responsible for making sure 539 // that the frame size is 8 mod 16. 540 if c.autosize == 8 { 541 c.autosize += 8 542 c.cursym.Func.Locals += 8 543 } else { 544 c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8) 545 } 546 } 547 p.To.Offset = int64(c.autosize) - 8 548 if c.autosize == 0 && !(c.cursym.Func.Text.Mark&LEAF != 0) { 549 if c.ctxt.Debugvlog { 550 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name) 551 } 552 c.cursym.Func.Text.Mark |= LEAF 553 } 554 555 if !p.From.Sym.NoSplit() { 556 p = c.stacksplit(p, c.autosize) // emit split check 557 } 558 559 aoffset = c.autosize 560 if aoffset > 0xF0 { 561 aoffset = 0xF0 562 } 563 if c.cursym.Func.Text.Mark&LEAF != 0 { 564 c.cursym.Set(obj.AttrLeaf, true) 565 if c.autosize == 0 { 566 break 567 } 568 } 569 570 // Frame is non-empty. Make sure to save link register, even if 571 // it is a leaf function, so that traceback works. 572 q = p 573 if c.autosize > aoffset { 574 // Frame size is too large for a MOVD.W instruction. 575 // Store link register before decrementing SP, so if a signal comes 576 // during the execution of the function prologue, the traceback 577 // code will not see a half-updated stack frame. 578 q = obj.Appendp(q, c.newprog) 579 q.Pos = p.Pos 580 q.As = ASUB 581 q.From.Type = obj.TYPE_CONST 582 q.From.Offset = int64(c.autosize) 583 q.Reg = REGSP 584 q.To.Type = obj.TYPE_REG 585 q.To.Reg = REGTMP 586 587 q = obj.Appendp(q, c.newprog) 588 q.Pos = p.Pos 589 q.As = AMOVD 590 q.From.Type = obj.TYPE_REG 591 q.From.Reg = REGLINK 592 q.To.Type = obj.TYPE_MEM 593 q.To.Reg = REGTMP 594 595 q1 = obj.Appendp(q, c.newprog) 596 q1.Pos = p.Pos 597 q1.As = AMOVD 598 q1.From.Type = obj.TYPE_REG 599 q1.From.Reg = REGTMP 600 q1.To.Type = obj.TYPE_REG 601 q1.To.Reg = REGSP 602 q1.Spadj = c.autosize 603 } else { 604 // small frame, update SP and save LR in a single MOVD.W instruction 605 q1 = obj.Appendp(q, c.newprog) 606 q1.As = AMOVD 607 q1.Pos = p.Pos 608 q1.From.Type = obj.TYPE_REG 609 q1.From.Reg = REGLINK 610 q1.To.Type = obj.TYPE_MEM 611 q1.Scond = C_XPRE 612 q1.To.Offset = int64(-aoffset) 613 q1.To.Reg = REGSP 614 q1.Spadj = aoffset 615 } 616 617 if c.cursym.Func.Text.From.Sym.Wrapper() { 618 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 619 // 620 // MOV g_panic(g), R1 621 // CBNZ checkargp 622 // end: 623 // NOP 624 // ... function body ... 625 // checkargp: 626 // MOV panic_argp(R1), R2 627 // ADD $(autosize+8), RSP, R3 628 // CMP R2, R3 629 // BNE end 630 // ADD $8, RSP, R4 631 // MOVD R4, panic_argp(R1) 632 // B end 633 // 634 // The NOP is needed to give the jumps somewhere to land. 635 // It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes. 636 q = q1 637 638 // MOV g_panic(g), R1 639 q = obj.Appendp(q, c.newprog) 640 q.As = AMOVD 641 q.From.Type = obj.TYPE_MEM 642 q.From.Reg = REGG 643 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 644 q.To.Type = obj.TYPE_REG 645 q.To.Reg = REG_R1 646 647 // CBNZ R1, checkargp 648 cbnz := obj.Appendp(q, c.newprog) 649 cbnz.As = ACBNZ 650 cbnz.From.Type = obj.TYPE_REG 651 cbnz.From.Reg = REG_R1 652 cbnz.To.Type = obj.TYPE_BRANCH 653 654 // Empty branch target at the top of the function body 655 end := obj.Appendp(cbnz, c.newprog) 656 end.As = obj.ANOP 657 658 // find the end of the function 659 var last *obj.Prog 660 for last = end; last.Link != nil; last = last.Link { 661 } 662 663 // MOV panic_argp(R1), R2 664 mov := obj.Appendp(last, c.newprog) 665 mov.As = AMOVD 666 mov.From.Type = obj.TYPE_MEM 667 mov.From.Reg = REG_R1 668 mov.From.Offset = 0 // Panic.argp 669 mov.To.Type = obj.TYPE_REG 670 mov.To.Reg = REG_R2 671 672 // CBNZ branches to the MOV above 673 cbnz.Pcond = mov 674 675 // ADD $(autosize+8), SP, R3 676 q = obj.Appendp(mov, c.newprog) 677 q.As = AADD 678 q.From.Type = obj.TYPE_CONST 679 q.From.Offset = int64(c.autosize) + 8 680 q.Reg = REGSP 681 q.To.Type = obj.TYPE_REG 682 q.To.Reg = REG_R3 683 684 // CMP R2, R3 685 q = obj.Appendp(q, c.newprog) 686 q.As = ACMP 687 q.From.Type = obj.TYPE_REG 688 q.From.Reg = REG_R2 689 q.Reg = REG_R3 690 691 // BNE end 692 q = obj.Appendp(q, c.newprog) 693 q.As = ABNE 694 q.To.Type = obj.TYPE_BRANCH 695 q.Pcond = end 696 697 // ADD $8, SP, R4 698 q = obj.Appendp(q, c.newprog) 699 q.As = AADD 700 q.From.Type = obj.TYPE_CONST 701 q.From.Offset = 8 702 q.Reg = REGSP 703 q.To.Type = obj.TYPE_REG 704 q.To.Reg = REG_R4 705 706 // MOV R4, panic_argp(R1) 707 q = obj.Appendp(q, c.newprog) 708 q.As = AMOVD 709 q.From.Type = obj.TYPE_REG 710 q.From.Reg = REG_R4 711 q.To.Type = obj.TYPE_MEM 712 q.To.Reg = REG_R1 713 q.To.Offset = 0 // Panic.argp 714 715 // B end 716 q = obj.Appendp(q, c.newprog) 717 q.As = AB 718 q.To.Type = obj.TYPE_BRANCH 719 q.Pcond = end 720 } 721 722 case obj.ARET: 723 nocache(p) 724 if p.From.Type == obj.TYPE_CONST { 725 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 726 break 727 } 728 729 retjmp = p.To.Sym 730 p.To = obj.Addr{} 731 if c.cursym.Func.Text.Mark&LEAF != 0 { 732 if c.autosize != 0 { 733 p.As = AADD 734 p.From.Type = obj.TYPE_CONST 735 p.From.Offset = int64(c.autosize) 736 p.To.Type = obj.TYPE_REG 737 p.To.Reg = REGSP 738 p.Spadj = -c.autosize 739 } 740 } else { 741 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ 742 aoffset = c.autosize 743 744 if aoffset > 0xF0 { 745 aoffset = 0xF0 746 } 747 p.As = AMOVD 748 p.From.Type = obj.TYPE_MEM 749 p.Scond = C_XPOST 750 p.From.Offset = int64(aoffset) 751 p.From.Reg = REGSP 752 p.To.Type = obj.TYPE_REG 753 p.To.Reg = REGLINK 754 p.Spadj = -aoffset 755 if c.autosize > aoffset { 756 q = newprog() 757 q.As = AADD 758 q.From.Type = obj.TYPE_CONST 759 q.From.Offset = int64(c.autosize) - int64(aoffset) 760 q.To.Type = obj.TYPE_REG 761 q.To.Reg = REGSP 762 q.Link = p.Link 763 q.Spadj = int32(-q.From.Offset) 764 q.Pos = p.Pos 765 p.Link = q 766 p = q 767 } 768 } 769 770 if p.As != obj.ARET { 771 q = newprog() 772 q.Pos = p.Pos 773 q.Link = p.Link 774 p.Link = q 775 p = q 776 } 777 778 if retjmp != nil { // retjmp 779 p.As = AB 780 p.To.Type = obj.TYPE_BRANCH 781 p.To.Sym = retjmp 782 p.Spadj = +c.autosize 783 break 784 } 785 786 p.As = obj.ARET 787 p.To.Type = obj.TYPE_MEM 788 p.To.Offset = 0 789 p.To.Reg = REGLINK 790 p.Spadj = +c.autosize 791 792 case AADD, ASUB: 793 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 794 if p.As == AADD { 795 p.Spadj = int32(-p.From.Offset) 796 } else { 797 p.Spadj = int32(+p.From.Offset) 798 } 799 } 800 break 801 } 802 } 803 } 804 805 func nocache(p *obj.Prog) { 806 p.Optab = 0 807 p.From.Class = 0 808 p.To.Class = 0 809 } 810 811 var unaryDst = map[obj.As]bool{ 812 AWORD: true, 813 ADWORD: true, 814 ABL: true, 815 AB: true, 816 ASVC: true, 817 } 818 819 var Linkarm64 = obj.LinkArch{ 820 Arch: sys.ArchARM64, 821 Init: buildop, 822 Preprocess: preprocess, 823 Assemble: span7, 824 Progedit: progedit, 825 UnaryDst: unaryDst, 826 }