github.com/tidwall/go@v0.0.0-20170415222209-6694a6888b7d/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 "math" 37 ) 38 39 var complements = []obj.As{ 40 AADD: ASUB, 41 AADDW: ASUBW, 42 ASUB: AADD, 43 ASUBW: AADDW, 44 ACMP: ACMN, 45 ACMPW: ACMNW, 46 ACMN: ACMP, 47 ACMNW: ACMPW, 48 } 49 50 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 51 // MOV g_stackguard(g), R1 52 p = obj.Appendp(p, c.newprog) 53 54 p.As = AMOVD 55 p.From.Type = obj.TYPE_MEM 56 p.From.Reg = REGG 57 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 58 if c.cursym.CFunc() { 59 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 60 } 61 p.To.Type = obj.TYPE_REG 62 p.To.Reg = REG_R1 63 64 q := (*obj.Prog)(nil) 65 if framesize <= obj.StackSmall { 66 // small stack: SP < stackguard 67 // MOV SP, R2 68 // CMP stackguard, R2 69 p = obj.Appendp(p, c.newprog) 70 71 p.As = AMOVD 72 p.From.Type = obj.TYPE_REG 73 p.From.Reg = REGSP 74 p.To.Type = obj.TYPE_REG 75 p.To.Reg = REG_R2 76 77 p = obj.Appendp(p, c.newprog) 78 p.As = ACMP 79 p.From.Type = obj.TYPE_REG 80 p.From.Reg = REG_R1 81 p.Reg = REG_R2 82 } else if framesize <= obj.StackBig { 83 // large stack: SP-framesize < stackguard-StackSmall 84 // SUB $(framesize-StackSmall), SP, R2 85 // CMP stackguard, R2 86 p = obj.Appendp(p, c.newprog) 87 88 p.As = ASUB 89 p.From.Type = obj.TYPE_CONST 90 p.From.Offset = int64(framesize) - obj.StackSmall 91 p.Reg = REGSP 92 p.To.Type = obj.TYPE_REG 93 p.To.Reg = REG_R2 94 95 p = obj.Appendp(p, c.newprog) 96 p.As = ACMP 97 p.From.Type = obj.TYPE_REG 98 p.From.Reg = REG_R1 99 p.Reg = REG_R2 100 } else { 101 // Such a large stack we need to protect against wraparound 102 // if SP is close to zero. 103 // SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall) 104 // The +StackGuard on both sides is required to keep the left side positive: 105 // SP is allowed to be slightly below stackguard. See stack.h. 106 // CMP $StackPreempt, R1 107 // BEQ label_of_call_to_morestack 108 // ADD $StackGuard, SP, R2 109 // SUB R1, R2 110 // MOV $(framesize+(StackGuard-StackSmall)), R3 111 // CMP R3, R2 112 p = obj.Appendp(p, c.newprog) 113 114 p.As = ACMP 115 p.From.Type = obj.TYPE_CONST 116 p.From.Offset = obj.StackPreempt 117 p.Reg = REG_R1 118 119 p = obj.Appendp(p, c.newprog) 120 q = p 121 p.As = ABEQ 122 p.To.Type = obj.TYPE_BRANCH 123 124 p = obj.Appendp(p, c.newprog) 125 p.As = AADD 126 p.From.Type = obj.TYPE_CONST 127 p.From.Offset = obj.StackGuard 128 p.Reg = REGSP 129 p.To.Type = obj.TYPE_REG 130 p.To.Reg = REG_R2 131 132 p = obj.Appendp(p, c.newprog) 133 p.As = ASUB 134 p.From.Type = obj.TYPE_REG 135 p.From.Reg = REG_R1 136 p.To.Type = obj.TYPE_REG 137 p.To.Reg = REG_R2 138 139 p = obj.Appendp(p, c.newprog) 140 p.As = AMOVD 141 p.From.Type = obj.TYPE_CONST 142 p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall) 143 p.To.Type = obj.TYPE_REG 144 p.To.Reg = REG_R3 145 146 p = obj.Appendp(p, c.newprog) 147 p.As = ACMP 148 p.From.Type = obj.TYPE_REG 149 p.From.Reg = REG_R3 150 p.Reg = REG_R2 151 } 152 153 // BLS do-morestack 154 bls := obj.Appendp(p, c.newprog) 155 bls.As = ABLS 156 bls.To.Type = obj.TYPE_BRANCH 157 158 var last *obj.Prog 159 for last = c.cursym.Text; last.Link != nil; last = last.Link { 160 } 161 162 // Now we are at the end of the function, but logically 163 // we are still in function prologue. We need to fix the 164 // SP data and PCDATA. 165 spfix := obj.Appendp(last, c.newprog) 166 spfix.As = obj.ANOP 167 spfix.Spadj = -framesize 168 169 pcdata := obj.Appendp(spfix, c.newprog) 170 pcdata.Pos = c.cursym.Text.Pos 171 pcdata.As = obj.APCDATA 172 pcdata.From.Type = obj.TYPE_CONST 173 pcdata.From.Offset = obj.PCDATA_StackMapIndex 174 pcdata.To.Type = obj.TYPE_CONST 175 pcdata.To.Offset = -1 // pcdata starts at -1 at function entry 176 177 // MOV LR, R3 178 movlr := obj.Appendp(pcdata, c.newprog) 179 movlr.As = AMOVD 180 movlr.From.Type = obj.TYPE_REG 181 movlr.From.Reg = REGLINK 182 movlr.To.Type = obj.TYPE_REG 183 movlr.To.Reg = REG_R3 184 if q != nil { 185 q.Pcond = movlr 186 } 187 bls.Pcond = movlr 188 189 debug := movlr 190 if false { 191 debug = obj.Appendp(debug, c.newprog) 192 debug.As = AMOVD 193 debug.From.Type = obj.TYPE_CONST 194 debug.From.Offset = int64(framesize) 195 debug.To.Type = obj.TYPE_REG 196 debug.To.Reg = REGTMP 197 } 198 199 // BL runtime.morestack(SB) 200 call := obj.Appendp(debug, c.newprog) 201 call.As = ABL 202 call.To.Type = obj.TYPE_BRANCH 203 morestack := "runtime.morestack" 204 switch { 205 case c.cursym.CFunc(): 206 morestack = "runtime.morestackc" 207 case !c.cursym.Text.From.Sym.NeedCtxt(): 208 morestack = "runtime.morestack_noctxt" 209 } 210 call.To.Sym = c.ctxt.Lookup(morestack, 0) 211 212 // B start 213 jmp := obj.Appendp(call, c.newprog) 214 jmp.As = AB 215 jmp.To.Type = obj.TYPE_BRANCH 216 jmp.Pcond = c.cursym.Text.Link 217 jmp.Spadj = +framesize 218 219 // placeholder for bls's jump target 220 // p = obj.Appendp(ctxt, p) 221 // p.As = obj.ANOP 222 223 return bls 224 } 225 226 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 227 c := ctxt7{ctxt: ctxt, newprog: newprog} 228 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 if math.Float32bits(f32) == 0 { 264 p.From.Type = obj.TYPE_REG 265 p.From.Reg = REGZERO 266 break 267 } 268 p.From.Type = obj.TYPE_MEM 269 p.From.Sym = c.ctxt.Float32Sym(f32) 270 p.From.Name = obj.NAME_EXTERN 271 p.From.Offset = 0 272 } 273 274 case AFMOVD: 275 if p.From.Type == obj.TYPE_FCONST { 276 f64 := p.From.Val.(float64) 277 if math.Float64bits(f64) == 0 { 278 p.From.Type = obj.TYPE_REG 279 p.From.Reg = REGZERO 280 break 281 } 282 p.From.Type = obj.TYPE_MEM 283 p.From.Sym = c.ctxt.Float64Sym(f64) 284 p.From.Name = obj.NAME_EXTERN 285 p.From.Offset = 0 286 } 287 288 break 289 } 290 291 // Rewrite negative immediates as positive immediates with 292 // complementary instruction. 293 switch p.As { 294 case AADD, ASUB, ACMP, ACMN: 295 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 { 296 p.From.Offset = -p.From.Offset 297 p.As = complements[p.As] 298 } 299 case AADDW, ASUBW, ACMPW, ACMNW: 300 if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 { 301 p.From.Offset = -p.From.Offset 302 p.As = complements[p.As] 303 } 304 } 305 306 // For 32-bit logical instruction with constant, 307 // rewrite the high 32-bit to be a repetition of 308 // the low 32-bit, so that the BITCON test can be 309 // shared for both 32-bit and 64-bit. 32-bit ops 310 // will zero the high 32-bit of the destination 311 // register anyway. 312 switch p.As { 313 case AANDW, AORRW, AEORW, AANDSW: 314 if p.From.Type == obj.TYPE_CONST { 315 v := p.From.Offset & 0xffffffff 316 p.From.Offset = v | v<<32 317 } 318 } 319 320 if c.ctxt.Flag_dynlink { 321 c.rewriteToUseGot(p) 322 } 323 } 324 325 // Rewrite p, if necessary, to access global data via the global offset table. 326 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) { 327 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 328 // ADUFFxxx $offset 329 // becomes 330 // MOVD runtime.duffxxx@GOT, REGTMP 331 // ADD $offset, REGTMP 332 // CALL REGTMP 333 var sym *obj.LSym 334 if p.As == obj.ADUFFZERO { 335 sym = c.ctxt.Lookup("runtime.duffzero", 0) 336 } else { 337 sym = c.ctxt.Lookup("runtime.duffcopy", 0) 338 } 339 offset := p.To.Offset 340 p.As = AMOVD 341 p.From.Type = obj.TYPE_MEM 342 p.From.Name = obj.NAME_GOTREF 343 p.From.Sym = sym 344 p.To.Type = obj.TYPE_REG 345 p.To.Reg = REGTMP 346 p.To.Name = obj.NAME_NONE 347 p.To.Offset = 0 348 p.To.Sym = nil 349 p1 := obj.Appendp(p, c.newprog) 350 p1.As = AADD 351 p1.From.Type = obj.TYPE_CONST 352 p1.From.Offset = offset 353 p1.To.Type = obj.TYPE_REG 354 p1.To.Reg = REGTMP 355 p2 := obj.Appendp(p1, c.newprog) 356 p2.As = obj.ACALL 357 p2.To.Type = obj.TYPE_REG 358 p2.To.Reg = REGTMP 359 } 360 361 // We only care about global data: NAME_EXTERN means a global 362 // symbol in the Go sense, and p.Sym.Local is true for a few 363 // internally defined symbols. 364 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 365 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 366 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 367 if p.As != AMOVD { 368 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 369 } 370 if p.To.Type != obj.TYPE_REG { 371 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 372 } 373 p.From.Type = obj.TYPE_MEM 374 p.From.Name = obj.NAME_GOTREF 375 if p.From.Offset != 0 { 376 q := obj.Appendp(p, c.newprog) 377 q.As = AADD 378 q.From.Type = obj.TYPE_CONST 379 q.From.Offset = p.From.Offset 380 q.To = p.To 381 p.From.Offset = 0 382 } 383 } 384 if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN { 385 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 386 } 387 var source *obj.Addr 388 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 389 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP) 390 // An addition may be inserted between the two MOVs if there is an offset. 391 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 392 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 393 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 394 } 395 source = &p.From 396 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 397 source = &p.To 398 } else { 399 return 400 } 401 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 402 return 403 } 404 if source.Sym.Type == obj.STLSBSS { 405 return 406 } 407 if source.Type != obj.TYPE_MEM { 408 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 409 } 410 p1 := obj.Appendp(p, c.newprog) 411 p2 := obj.Appendp(p1, c.newprog) 412 p1.As = AMOVD 413 p1.From.Type = obj.TYPE_MEM 414 p1.From.Sym = source.Sym 415 p1.From.Name = obj.NAME_GOTREF 416 p1.To.Type = obj.TYPE_REG 417 p1.To.Reg = REGTMP 418 419 p2.As = p.As 420 p2.From = p.From 421 p2.To = p.To 422 if p.From.Name == obj.NAME_EXTERN { 423 p2.From.Reg = REGTMP 424 p2.From.Name = obj.NAME_NONE 425 p2.From.Sym = nil 426 } else if p.To.Name == obj.NAME_EXTERN { 427 p2.To.Reg = REGTMP 428 p2.To.Name = obj.NAME_NONE 429 p2.To.Sym = nil 430 } else { 431 return 432 } 433 obj.Nopout(p) 434 } 435 436 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 437 if cursym.Text == nil || cursym.Text.Link == nil { 438 return 439 } 440 441 c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym} 442 443 p := c.cursym.Text 444 textstksiz := p.To.Offset 445 aoffset := int32(textstksiz) 446 447 c.cursym.Args = p.To.Val.(int32) 448 c.cursym.Locals = int32(textstksiz) 449 450 /* 451 * find leaf subroutines 452 * strip NOPs 453 * expand RET 454 */ 455 q := (*obj.Prog)(nil) 456 var q1 *obj.Prog 457 for p := c.cursym.Text; p != nil; p = p.Link { 458 switch p.As { 459 case obj.ATEXT: 460 p.Mark |= LEAF 461 462 case obj.ARET: 463 break 464 465 case obj.ANOP: 466 q1 = p.Link 467 q.Link = q1 /* q is non-nop */ 468 q1.Mark |= p.Mark 469 continue 470 471 case ABL, 472 obj.ADUFFZERO, 473 obj.ADUFFCOPY: 474 c.cursym.Text.Mark &^= LEAF 475 fallthrough 476 477 case ACBNZ, 478 ACBZ, 479 ACBNZW, 480 ACBZW, 481 ATBZ, 482 ATBNZ, 483 AB, 484 ABEQ, 485 ABNE, 486 ABCS, 487 ABHS, 488 ABCC, 489 ABLO, 490 ABMI, 491 ABPL, 492 ABVS, 493 ABVC, 494 ABHI, 495 ABLS, 496 ABGE, 497 ABLT, 498 ABGT, 499 ABLE, 500 AADR, /* strange */ 501 AADRP: 502 q1 = p.Pcond 503 504 if q1 != nil { 505 for q1.As == obj.ANOP { 506 q1 = q1.Link 507 p.Pcond = q1 508 } 509 } 510 511 break 512 } 513 514 q = p 515 } 516 517 var q2 *obj.Prog 518 var retjmp *obj.LSym 519 for p := c.cursym.Text; p != nil; p = p.Link { 520 o := p.As 521 switch o { 522 case obj.ATEXT: 523 c.cursym.Text = p 524 if textstksiz < 0 { 525 c.autosize = 0 526 } else { 527 c.autosize = int32(textstksiz + 8) 528 } 529 if (c.cursym.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.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.Text.Mark&LEAF != 0) { 549 if c.ctxt.Debugvlog { 550 c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Text.From.Sym.Name) 551 } 552 c.cursym.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.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.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 // CMP ZR, R1 622 // BEQ end 623 // MOV panic_argp(R1), R2 624 // ADD $(autosize+8), RSP, R3 625 // CMP R2, R3 626 // BNE end 627 // ADD $8, RSP, R4 628 // MOVD R4, panic_argp(R1) 629 // end: 630 // NOP 631 // 632 // The NOP is needed to give the jumps somewhere to land. 633 // It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes. 634 q = q1 635 636 q = obj.Appendp(q, c.newprog) 637 q.As = AMOVD 638 q.From.Type = obj.TYPE_MEM 639 q.From.Reg = REGG 640 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 641 q.To.Type = obj.TYPE_REG 642 q.To.Reg = REG_R1 643 644 q = obj.Appendp(q, c.newprog) 645 q.As = ACMP 646 q.From.Type = obj.TYPE_REG 647 q.From.Reg = REGZERO 648 q.Reg = REG_R1 649 650 q = obj.Appendp(q, c.newprog) 651 q.As = ABEQ 652 q.To.Type = obj.TYPE_BRANCH 653 q1 = q 654 655 q = obj.Appendp(q, c.newprog) 656 q.As = AMOVD 657 q.From.Type = obj.TYPE_MEM 658 q.From.Reg = REG_R1 659 q.From.Offset = 0 // Panic.argp 660 q.To.Type = obj.TYPE_REG 661 q.To.Reg = REG_R2 662 663 q = obj.Appendp(q, c.newprog) 664 q.As = AADD 665 q.From.Type = obj.TYPE_CONST 666 q.From.Offset = int64(c.autosize) + 8 667 q.Reg = REGSP 668 q.To.Type = obj.TYPE_REG 669 q.To.Reg = REG_R3 670 671 q = obj.Appendp(q, c.newprog) 672 q.As = ACMP 673 q.From.Type = obj.TYPE_REG 674 q.From.Reg = REG_R2 675 q.Reg = REG_R3 676 677 q = obj.Appendp(q, c.newprog) 678 q.As = ABNE 679 q.To.Type = obj.TYPE_BRANCH 680 q2 = q 681 682 q = obj.Appendp(q, c.newprog) 683 q.As = AADD 684 q.From.Type = obj.TYPE_CONST 685 q.From.Offset = 8 686 q.Reg = REGSP 687 q.To.Type = obj.TYPE_REG 688 q.To.Reg = REG_R4 689 690 q = obj.Appendp(q, c.newprog) 691 q.As = AMOVD 692 q.From.Type = obj.TYPE_REG 693 q.From.Reg = REG_R4 694 q.To.Type = obj.TYPE_MEM 695 q.To.Reg = REG_R1 696 q.To.Offset = 0 // Panic.argp 697 698 q = obj.Appendp(q, c.newprog) 699 700 q.As = obj.ANOP 701 q1.Pcond = q 702 q2.Pcond = q 703 } 704 705 case obj.ARET: 706 nocache(p) 707 if p.From.Type == obj.TYPE_CONST { 708 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 709 break 710 } 711 712 retjmp = p.To.Sym 713 p.To = obj.Addr{} 714 if c.cursym.Text.Mark&LEAF != 0 { 715 if c.autosize != 0 { 716 p.As = AADD 717 p.From.Type = obj.TYPE_CONST 718 p.From.Offset = int64(c.autosize) 719 p.To.Type = obj.TYPE_REG 720 p.To.Reg = REGSP 721 p.Spadj = -c.autosize 722 } 723 } else { 724 /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ 725 aoffset = c.autosize 726 727 if aoffset > 0xF0 { 728 aoffset = 0xF0 729 } 730 p.As = AMOVD 731 p.From.Type = obj.TYPE_MEM 732 p.Scond = C_XPOST 733 p.From.Offset = int64(aoffset) 734 p.From.Reg = REGSP 735 p.To.Type = obj.TYPE_REG 736 p.To.Reg = REGLINK 737 p.Spadj = -aoffset 738 if c.autosize > aoffset { 739 q = newprog() 740 q.As = AADD 741 q.From.Type = obj.TYPE_CONST 742 q.From.Offset = int64(c.autosize) - int64(aoffset) 743 q.To.Type = obj.TYPE_REG 744 q.To.Reg = REGSP 745 q.Link = p.Link 746 q.Spadj = int32(-q.From.Offset) 747 q.Pos = p.Pos 748 p.Link = q 749 p = q 750 } 751 } 752 753 if p.As != obj.ARET { 754 q = newprog() 755 q.Pos = p.Pos 756 q.Link = p.Link 757 p.Link = q 758 p = q 759 } 760 761 if retjmp != nil { // retjmp 762 p.As = AB 763 p.To.Type = obj.TYPE_BRANCH 764 p.To.Sym = retjmp 765 p.Spadj = +c.autosize 766 break 767 } 768 769 p.As = obj.ARET 770 p.To.Type = obj.TYPE_MEM 771 p.To.Offset = 0 772 p.To.Reg = REGLINK 773 p.Spadj = +c.autosize 774 775 case AADD, ASUB: 776 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 777 if p.As == AADD { 778 p.Spadj = int32(-p.From.Offset) 779 } else { 780 p.Spadj = int32(+p.From.Offset) 781 } 782 } 783 break 784 } 785 } 786 } 787 788 func nocache(p *obj.Prog) { 789 p.Optab = 0 790 p.From.Class = 0 791 p.To.Class = 0 792 } 793 794 var unaryDst = map[obj.As]bool{ 795 AWORD: true, 796 ADWORD: true, 797 ABL: true, 798 AB: true, 799 ASVC: true, 800 } 801 802 var Linkarm64 = obj.LinkArch{ 803 Arch: sys.ArchARM64, 804 Init: buildop, 805 Preprocess: preprocess, 806 Assemble: span7, 807 Progedit: progedit, 808 UnaryDst: unaryDst, 809 }