github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/internal/obj/ppc64/obj9.go (about) 1 // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova. 2 // 3 // Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. 4 // Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) 5 // Portions Copyright © 1997-1999 Vita Nuova Limited 6 // Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com) 7 // Portions Copyright © 2004,2006 Bruce Ellis 8 // Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) 9 // Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others 10 // Portions Copyright © 2009 The Go Authors. All rights reserved. 11 // 12 // Permission is hereby granted, free of charge, to any person obtaining a copy 13 // of this software and associated documentation files (the "Software"), to deal 14 // in the Software without restriction, including without limitation the rights 15 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 16 // copies of the Software, and to permit persons to whom the Software is 17 // furnished to do so, subject to the following conditions: 18 // 19 // The above copyright notice and this permission notice shall be included in 20 // all copies or substantial portions of the Software. 21 // 22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 27 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 28 // THE SOFTWARE. 29 30 package ppc64 31 32 import ( 33 "cmd/internal/obj" 34 "cmd/internal/objabi" 35 "cmd/internal/sys" 36 ) 37 38 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 39 p.From.Class = 0 40 p.To.Class = 0 41 42 c := ctxt9{ctxt: ctxt, newprog: newprog} 43 44 // Rewrite BR/BL to symbol as TYPE_BRANCH. 45 switch p.As { 46 case ABR, 47 ABL, 48 obj.ARET, 49 obj.ADUFFZERO, 50 obj.ADUFFCOPY: 51 if p.To.Sym != nil { 52 p.To.Type = obj.TYPE_BRANCH 53 } 54 } 55 56 // Rewrite float constants to values stored in memory. 57 switch p.As { 58 case AFMOVS: 59 if p.From.Type == obj.TYPE_FCONST { 60 f32 := float32(p.From.Val.(float64)) 61 p.From.Type = obj.TYPE_MEM 62 p.From.Sym = ctxt.Float32Sym(f32) 63 p.From.Name = obj.NAME_EXTERN 64 p.From.Offset = 0 65 } 66 67 case AFMOVD: 68 if p.From.Type == obj.TYPE_FCONST { 69 f64 := p.From.Val.(float64) 70 // Constant not needed in memory for float +/- 0 71 if f64 != 0 { 72 p.From.Type = obj.TYPE_MEM 73 p.From.Sym = ctxt.Float64Sym(f64) 74 p.From.Name = obj.NAME_EXTERN 75 p.From.Offset = 0 76 } 77 } 78 79 // Put >32-bit constants in memory and load them 80 case AMOVD: 81 if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset { 82 p.From.Type = obj.TYPE_MEM 83 p.From.Sym = ctxt.Int64Sym(p.From.Offset) 84 p.From.Name = obj.NAME_EXTERN 85 p.From.Offset = 0 86 } 87 } 88 89 // Rewrite SUB constants into ADD. 90 switch p.As { 91 case ASUBC: 92 if p.From.Type == obj.TYPE_CONST { 93 p.From.Offset = -p.From.Offset 94 p.As = AADDC 95 } 96 97 case ASUBCCC: 98 if p.From.Type == obj.TYPE_CONST { 99 p.From.Offset = -p.From.Offset 100 p.As = AADDCCC 101 } 102 103 case ASUB: 104 if p.From.Type == obj.TYPE_CONST { 105 p.From.Offset = -p.From.Offset 106 p.As = AADD 107 } 108 } 109 if c.ctxt.Flag_dynlink { 110 c.rewriteToUseGot(p) 111 } 112 } 113 114 // Rewrite p, if necessary, to access global data via the global offset table. 115 func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { 116 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 117 // ADUFFxxx $offset 118 // becomes 119 // MOVD runtime.duffxxx@GOT, R12 120 // ADD $offset, R12 121 // MOVD R12, CTR 122 // BL (CTR) 123 var sym *obj.LSym 124 if p.As == obj.ADUFFZERO { 125 sym = c.ctxt.Lookup("runtime.duffzero") 126 } else { 127 sym = c.ctxt.Lookup("runtime.duffcopy") 128 } 129 offset := p.To.Offset 130 p.As = AMOVD 131 p.From.Type = obj.TYPE_MEM 132 p.From.Name = obj.NAME_GOTREF 133 p.From.Sym = sym 134 p.To.Type = obj.TYPE_REG 135 p.To.Reg = REG_R12 136 p.To.Name = obj.NAME_NONE 137 p.To.Offset = 0 138 p.To.Sym = nil 139 p1 := obj.Appendp(p, c.newprog) 140 p1.As = AADD 141 p1.From.Type = obj.TYPE_CONST 142 p1.From.Offset = offset 143 p1.To.Type = obj.TYPE_REG 144 p1.To.Reg = REG_R12 145 p2 := obj.Appendp(p1, c.newprog) 146 p2.As = AMOVD 147 p2.From.Type = obj.TYPE_REG 148 p2.From.Reg = REG_R12 149 p2.To.Type = obj.TYPE_REG 150 p2.To.Reg = REG_CTR 151 p3 := obj.Appendp(p2, c.newprog) 152 p3.As = obj.ACALL 153 p3.From.Type = obj.TYPE_REG 154 p3.From.Reg = REG_R12 155 p3.To.Type = obj.TYPE_REG 156 p3.To.Reg = REG_CTR 157 } 158 159 // We only care about global data: NAME_EXTERN means a global 160 // symbol in the Go sense, and p.Sym.Local is true for a few 161 // internally defined symbols. 162 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 163 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 164 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 165 if p.As != AMOVD { 166 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 167 } 168 if p.To.Type != obj.TYPE_REG { 169 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 170 } 171 p.From.Type = obj.TYPE_MEM 172 p.From.Name = obj.NAME_GOTREF 173 if p.From.Offset != 0 { 174 q := obj.Appendp(p, c.newprog) 175 q.As = AADD 176 q.From.Type = obj.TYPE_CONST 177 q.From.Offset = p.From.Offset 178 q.To = p.To 179 p.From.Offset = 0 180 } 181 } 182 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 183 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 184 } 185 var source *obj.Addr 186 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 187 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP) 188 // An addition may be inserted between the two MOVs if there is an offset. 189 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 190 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 191 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 192 } 193 source = &p.From 194 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 195 source = &p.To 196 } else { 197 return 198 } 199 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 200 return 201 } 202 if source.Sym.Type == objabi.STLSBSS { 203 return 204 } 205 if source.Type != obj.TYPE_MEM { 206 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 207 } 208 p1 := obj.Appendp(p, c.newprog) 209 p2 := obj.Appendp(p1, c.newprog) 210 211 p1.As = AMOVD 212 p1.From.Type = obj.TYPE_MEM 213 p1.From.Sym = source.Sym 214 p1.From.Name = obj.NAME_GOTREF 215 p1.To.Type = obj.TYPE_REG 216 p1.To.Reg = REGTMP 217 218 p2.As = p.As 219 p2.From = p.From 220 p2.To = p.To 221 if p.From.Name == obj.NAME_EXTERN { 222 p2.From.Reg = REGTMP 223 p2.From.Name = obj.NAME_NONE 224 p2.From.Sym = nil 225 } else if p.To.Name == obj.NAME_EXTERN { 226 p2.To.Reg = REGTMP 227 p2.To.Name = obj.NAME_NONE 228 p2.To.Sym = nil 229 } else { 230 return 231 } 232 obj.Nopout(p) 233 } 234 235 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 236 // TODO(minux): add morestack short-cuts with small fixed frame-size. 237 if cursym.Func.Text == nil || cursym.Func.Text.Link == nil { 238 return 239 } 240 241 c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog} 242 243 p := c.cursym.Func.Text 244 textstksiz := p.To.Offset 245 if textstksiz == -8 { 246 // Compatibility hack. 247 p.From.Sym.Set(obj.AttrNoFrame, true) 248 textstksiz = 0 249 } 250 if textstksiz%8 != 0 { 251 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz) 252 } 253 if p.From.Sym.NoFrame() { 254 if textstksiz != 0 { 255 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 256 } 257 } 258 259 c.cursym.Func.Args = p.To.Val.(int32) 260 c.cursym.Func.Locals = int32(textstksiz) 261 262 /* 263 * find leaf subroutines 264 * strip NOPs 265 * expand RET 266 * expand BECOME pseudo 267 */ 268 269 var q *obj.Prog 270 var q1 *obj.Prog 271 for p := c.cursym.Func.Text; p != nil; p = p.Link { 272 switch p.As { 273 /* too hard, just leave alone */ 274 case obj.ATEXT: 275 q = p 276 277 p.Mark |= LABEL | LEAF | SYNC 278 if p.Link != nil { 279 p.Link.Mark |= LABEL 280 } 281 282 case ANOR: 283 q = p 284 if p.To.Type == obj.TYPE_REG { 285 if p.To.Reg == REGZERO { 286 p.Mark |= LABEL | SYNC 287 } 288 } 289 290 case ALWAR, 291 ALBAR, 292 ASTBCCC, 293 ASTWCCC, 294 AECIWX, 295 AECOWX, 296 AEIEIO, 297 AICBI, 298 AISYNC, 299 ATLBIE, 300 ATLBIEL, 301 ASLBIA, 302 ASLBIE, 303 ASLBMFEE, 304 ASLBMFEV, 305 ASLBMTE, 306 ADCBF, 307 ADCBI, 308 ADCBST, 309 ADCBT, 310 ADCBTST, 311 ADCBZ, 312 ASYNC, 313 ATLBSYNC, 314 APTESYNC, 315 ALWSYNC, 316 ATW, 317 AWORD, 318 ARFI, 319 ARFCI, 320 ARFID, 321 AHRFID: 322 q = p 323 p.Mark |= LABEL | SYNC 324 continue 325 326 case AMOVW, AMOVWZ, AMOVD: 327 q = p 328 if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { 329 p.Mark |= LABEL | SYNC 330 } 331 continue 332 333 case AFABS, 334 AFABSCC, 335 AFADD, 336 AFADDCC, 337 AFCTIW, 338 AFCTIWCC, 339 AFCTIWZ, 340 AFCTIWZCC, 341 AFDIV, 342 AFDIVCC, 343 AFMADD, 344 AFMADDCC, 345 AFMOVD, 346 AFMOVDU, 347 /* case AFMOVDS: */ 348 AFMOVS, 349 AFMOVSU, 350 351 /* case AFMOVSD: */ 352 AFMSUB, 353 AFMSUBCC, 354 AFMUL, 355 AFMULCC, 356 AFNABS, 357 AFNABSCC, 358 AFNEG, 359 AFNEGCC, 360 AFNMADD, 361 AFNMADDCC, 362 AFNMSUB, 363 AFNMSUBCC, 364 AFRSP, 365 AFRSPCC, 366 AFSUB, 367 AFSUBCC: 368 q = p 369 370 p.Mark |= FLOAT 371 continue 372 373 case ABL, 374 ABCL, 375 obj.ADUFFZERO, 376 obj.ADUFFCOPY: 377 c.cursym.Func.Text.Mark &^= LEAF 378 fallthrough 379 380 case ABC, 381 ABEQ, 382 ABGE, 383 ABGT, 384 ABLE, 385 ABLT, 386 ABNE, 387 ABR, 388 ABVC, 389 ABVS: 390 p.Mark |= BRANCH 391 q = p 392 q1 = p.Pcond 393 if q1 != nil { 394 for q1.As == obj.ANOP { 395 q1 = q1.Link 396 p.Pcond = q1 397 } 398 399 if q1.Mark&LEAF == 0 { 400 q1.Mark |= LABEL 401 } 402 } else { 403 p.Mark |= LABEL 404 } 405 q1 = p.Link 406 if q1 != nil { 407 q1.Mark |= LABEL 408 } 409 continue 410 411 case AFCMPO, AFCMPU: 412 q = p 413 p.Mark |= FCMP | FLOAT 414 continue 415 416 case obj.ARET: 417 q = p 418 if p.Link != nil { 419 p.Link.Mark |= LABEL 420 } 421 continue 422 423 case obj.ANOP: 424 q1 = p.Link 425 q.Link = q1 /* q is non-nop */ 426 q1.Mark |= p.Mark 427 continue 428 429 default: 430 q = p 431 continue 432 } 433 } 434 435 autosize := int32(0) 436 var p1 *obj.Prog 437 var p2 *obj.Prog 438 for p := c.cursym.Func.Text; p != nil; p = p.Link { 439 o := p.As 440 switch o { 441 case obj.ATEXT: 442 autosize = int32(textstksiz) 443 444 if p.Mark&LEAF != 0 && autosize == 0 { 445 // A leaf function with no locals has no frame. 446 p.From.Sym.Set(obj.AttrNoFrame, true) 447 } 448 449 if !p.From.Sym.NoFrame() { 450 // If there is a stack frame at all, it includes 451 // space to save the LR. 452 autosize += int32(c.ctxt.FixedFrameSize()) 453 } 454 455 if p.Mark&LEAF != 0 && autosize < objabi.StackSmall { 456 // A leaf function with a small stack can be marked 457 // NOSPLIT, avoiding a stack check. 458 p.From.Sym.Set(obj.AttrNoSplit, true) 459 } 460 461 p.To.Offset = int64(autosize) 462 463 q = p 464 465 if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" { 466 // When compiling Go into PIC, all functions must start 467 // with instructions to load the TOC pointer into r2: 468 // 469 // addis r2, r12, .TOC.-func@ha 470 // addi r2, r2, .TOC.-func@l+4 471 // 472 // We could probably skip this prologue in some situations 473 // but it's a bit subtle. However, it is both safe and 474 // necessary to leave the prologue off duffzero and 475 // duffcopy as we rely on being able to jump to a specific 476 // instruction offset for them. 477 // 478 // These are AWORDS because there is no (afaict) way to 479 // generate the addis instruction except as part of the 480 // load of a large constant, and in that case there is no 481 // way to use r12 as the source. 482 // 483 // Note that the same condition is tested in 484 // putelfsym in cmd/link/internal/ld/symtab.go 485 // where we set the st_other field to indicate 486 // the presence of these instructions. 487 q = obj.Appendp(q, c.newprog) 488 q.As = AWORD 489 q.Pos = p.Pos 490 q.From.Type = obj.TYPE_CONST 491 q.From.Offset = 0x3c4c0000 492 q = obj.Appendp(q, c.newprog) 493 q.As = AWORD 494 q.Pos = p.Pos 495 q.From.Type = obj.TYPE_CONST 496 q.From.Offset = 0x38420000 497 rel := obj.Addrel(c.cursym) 498 rel.Off = 0 499 rel.Siz = 8 500 rel.Sym = c.ctxt.Lookup(".TOC.") 501 rel.Type = objabi.R_ADDRPOWER_PCREL 502 } 503 504 if !c.cursym.Func.Text.From.Sym.NoSplit() { 505 q = c.stacksplit(q, autosize) // emit split check 506 } 507 508 // Special handling of the racecall thunk. Assume that its asm code will 509 // save the link register and update the stack, since that code is 510 // called directly from C/C++ and can't clobber REGTMP (R31). 511 if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { 512 // Save the link register and update the SP. MOVDU is used unless 513 // the frame size is too large. The link register must be saved 514 // even for non-empty leaf functions so that traceback works. 515 if autosize >= -BIG && autosize <= BIG { 516 // Use MOVDU to adjust R1 when saving R31, if autosize is small. 517 q = obj.Appendp(q, c.newprog) 518 q.As = AMOVD 519 q.Pos = p.Pos 520 q.From.Type = obj.TYPE_REG 521 q.From.Reg = REG_LR 522 q.To.Type = obj.TYPE_REG 523 q.To.Reg = REGTMP 524 525 q = obj.Appendp(q, c.newprog) 526 q.As = AMOVDU 527 q.Pos = p.Pos 528 q.From.Type = obj.TYPE_REG 529 q.From.Reg = REGTMP 530 q.To.Type = obj.TYPE_MEM 531 q.To.Offset = int64(-autosize) 532 q.To.Reg = REGSP 533 q.Spadj = autosize 534 } else { 535 // Frame size is too large for a MOVDU instruction. 536 // Store link register before decrementing SP, so if a signal comes 537 // during the execution of the function prologue, the traceback 538 // code will not see a half-updated stack frame. 539 q = obj.Appendp(q, c.newprog) 540 q.As = AMOVD 541 q.Pos = p.Pos 542 q.From.Type = obj.TYPE_REG 543 q.From.Reg = REG_LR 544 q.To.Type = obj.TYPE_REG 545 q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction 546 547 q = obj.Appendp(q, c.newprog) 548 q.As = AMOVD 549 q.Pos = p.Pos 550 q.From.Type = obj.TYPE_REG 551 q.From.Reg = REG_R29 552 q.To.Type = obj.TYPE_MEM 553 q.To.Offset = int64(-autosize) 554 q.To.Reg = REGSP 555 556 q = obj.Appendp(q, c.newprog) 557 q.As = AADD 558 q.Pos = p.Pos 559 q.From.Type = obj.TYPE_CONST 560 q.From.Offset = int64(-autosize) 561 q.To.Type = obj.TYPE_REG 562 q.To.Reg = REGSP 563 q.Spadj = +autosize 564 } 565 } else if c.cursym.Func.Text.Mark&LEAF == 0 { 566 // A very few functions that do not return to their caller 567 // (e.g. gogo) are not identified as leaves but still have 568 // no frame. 569 c.cursym.Func.Text.Mark |= LEAF 570 } 571 572 if c.cursym.Func.Text.Mark&LEAF != 0 { 573 c.cursym.Set(obj.AttrLeaf, true) 574 break 575 } 576 577 if c.ctxt.Flag_shared { 578 q = obj.Appendp(q, c.newprog) 579 q.As = AMOVD 580 q.Pos = p.Pos 581 q.From.Type = obj.TYPE_REG 582 q.From.Reg = REG_R2 583 q.To.Type = obj.TYPE_MEM 584 q.To.Reg = REGSP 585 q.To.Offset = 24 586 } 587 588 if c.cursym.Func.Text.From.Sym.Wrapper() { 589 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 590 // 591 // MOVD g_panic(g), R3 592 // CMP R0, R3 593 // BEQ end 594 // MOVD panic_argp(R3), R4 595 // ADD $(autosize+8), R1, R5 596 // CMP R4, R5 597 // BNE end 598 // ADD $8, R1, R6 599 // MOVD R6, panic_argp(R3) 600 // end: 601 // NOP 602 // 603 // The NOP is needed to give the jumps somewhere to land. 604 // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. 605 606 q = obj.Appendp(q, c.newprog) 607 608 q.As = AMOVD 609 q.From.Type = obj.TYPE_MEM 610 q.From.Reg = REGG 611 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 612 q.To.Type = obj.TYPE_REG 613 q.To.Reg = REG_R3 614 615 q = obj.Appendp(q, c.newprog) 616 q.As = ACMP 617 q.From.Type = obj.TYPE_REG 618 q.From.Reg = REG_R0 619 q.To.Type = obj.TYPE_REG 620 q.To.Reg = REG_R3 621 622 q = obj.Appendp(q, c.newprog) 623 q.As = ABEQ 624 q.To.Type = obj.TYPE_BRANCH 625 p1 = q 626 627 q = obj.Appendp(q, c.newprog) 628 q.As = AMOVD 629 q.From.Type = obj.TYPE_MEM 630 q.From.Reg = REG_R3 631 q.From.Offset = 0 // Panic.argp 632 q.To.Type = obj.TYPE_REG 633 q.To.Reg = REG_R4 634 635 q = obj.Appendp(q, c.newprog) 636 q.As = AADD 637 q.From.Type = obj.TYPE_CONST 638 q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize() 639 q.Reg = REGSP 640 q.To.Type = obj.TYPE_REG 641 q.To.Reg = REG_R5 642 643 q = obj.Appendp(q, c.newprog) 644 q.As = ACMP 645 q.From.Type = obj.TYPE_REG 646 q.From.Reg = REG_R4 647 q.To.Type = obj.TYPE_REG 648 q.To.Reg = REG_R5 649 650 q = obj.Appendp(q, c.newprog) 651 q.As = ABNE 652 q.To.Type = obj.TYPE_BRANCH 653 p2 = q 654 655 q = obj.Appendp(q, c.newprog) 656 q.As = AADD 657 q.From.Type = obj.TYPE_CONST 658 q.From.Offset = c.ctxt.FixedFrameSize() 659 q.Reg = REGSP 660 q.To.Type = obj.TYPE_REG 661 q.To.Reg = REG_R6 662 663 q = obj.Appendp(q, c.newprog) 664 q.As = AMOVD 665 q.From.Type = obj.TYPE_REG 666 q.From.Reg = REG_R6 667 q.To.Type = obj.TYPE_MEM 668 q.To.Reg = REG_R3 669 q.To.Offset = 0 // Panic.argp 670 671 q = obj.Appendp(q, c.newprog) 672 673 q.As = obj.ANOP 674 p1.Pcond = q 675 p2.Pcond = q 676 } 677 678 case obj.ARET: 679 if p.From.Type == obj.TYPE_CONST { 680 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 681 break 682 } 683 684 retTarget := p.To.Sym 685 686 if c.cursym.Func.Text.Mark&LEAF != 0 { 687 if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" { 688 p.As = ABR 689 p.From = obj.Addr{} 690 if retTarget == nil { 691 p.To.Type = obj.TYPE_REG 692 p.To.Reg = REG_LR 693 } else { 694 p.To.Type = obj.TYPE_BRANCH 695 p.To.Sym = retTarget 696 } 697 p.Mark |= BRANCH 698 break 699 } 700 701 p.As = AADD 702 p.From.Type = obj.TYPE_CONST 703 p.From.Offset = int64(autosize) 704 p.To.Type = obj.TYPE_REG 705 p.To.Reg = REGSP 706 p.Spadj = -autosize 707 708 q = c.newprog() 709 q.As = ABR 710 q.Pos = p.Pos 711 q.To.Type = obj.TYPE_REG 712 q.To.Reg = REG_LR 713 q.Mark |= BRANCH 714 q.Spadj = +autosize 715 716 q.Link = p.Link 717 p.Link = q 718 break 719 } 720 721 p.As = AMOVD 722 p.From.Type = obj.TYPE_MEM 723 p.From.Offset = 0 724 p.From.Reg = REGSP 725 p.To.Type = obj.TYPE_REG 726 p.To.Reg = REGTMP 727 728 q = c.newprog() 729 q.As = AMOVD 730 q.Pos = p.Pos 731 q.From.Type = obj.TYPE_REG 732 q.From.Reg = REGTMP 733 q.To.Type = obj.TYPE_REG 734 q.To.Reg = REG_LR 735 736 q.Link = p.Link 737 p.Link = q 738 p = q 739 740 if false { 741 // Debug bad returns 742 q = c.newprog() 743 744 q.As = AMOVD 745 q.Pos = p.Pos 746 q.From.Type = obj.TYPE_MEM 747 q.From.Offset = 0 748 q.From.Reg = REGTMP 749 q.To.Type = obj.TYPE_REG 750 q.To.Reg = REGTMP 751 752 q.Link = p.Link 753 p.Link = q 754 p = q 755 } 756 prev := p 757 if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { 758 q = c.newprog() 759 q.As = AADD 760 q.Pos = p.Pos 761 q.From.Type = obj.TYPE_CONST 762 q.From.Offset = int64(autosize) 763 q.To.Type = obj.TYPE_REG 764 q.To.Reg = REGSP 765 q.Spadj = -autosize 766 767 q.Link = p.Link 768 prev.Link = q 769 prev = q 770 } 771 772 q1 = c.newprog() 773 q1.As = ABR 774 q1.Pos = p.Pos 775 if retTarget == nil { 776 q1.To.Type = obj.TYPE_REG 777 q1.To.Reg = REG_LR 778 } else { 779 q1.To.Type = obj.TYPE_BRANCH 780 q1.To.Sym = retTarget 781 } 782 q1.Mark |= BRANCH 783 q1.Spadj = +autosize 784 785 q1.Link = q.Link 786 prev.Link = q1 787 case AADD: 788 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 789 p.Spadj = int32(-p.From.Offset) 790 } 791 case obj.AGETCALLERPC: 792 if cursym.Leaf() { 793 /* MOVD LR, Rd */ 794 p.As = AMOVD 795 p.From.Type = obj.TYPE_REG 796 p.From.Reg = REG_LR 797 } else { 798 /* MOVD (RSP), Rd */ 799 p.As = AMOVD 800 p.From.Type = obj.TYPE_MEM 801 p.From.Reg = REGSP 802 } 803 } 804 } 805 } 806 807 /* 808 // instruction scheduling 809 if(debug['Q'] == 0) 810 return; 811 812 curtext = nil; 813 q = nil; // p - 1 814 q1 = firstp; // top of block 815 o = 0; // count of instructions 816 for(p = firstp; p != nil; p = p1) { 817 p1 = p->link; 818 o++; 819 if(p->mark & NOSCHED){ 820 if(q1 != p){ 821 sched(q1, q); 822 } 823 for(; p != nil; p = p->link){ 824 if(!(p->mark & NOSCHED)) 825 break; 826 q = p; 827 } 828 p1 = p; 829 q1 = p; 830 o = 0; 831 continue; 832 } 833 if(p->mark & (LABEL|SYNC)) { 834 if(q1 != p) 835 sched(q1, q); 836 q1 = p; 837 o = 1; 838 } 839 if(p->mark & (BRANCH|SYNC)) { 840 sched(q1, p); 841 q1 = p1; 842 o = 0; 843 } 844 if(o >= NSCHED) { 845 sched(q1, p); 846 q1 = p1; 847 o = 0; 848 } 849 q = p; 850 } 851 */ 852 func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 853 p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode 854 855 // MOVD g_stackguard(g), R3 856 p = obj.Appendp(p, c.newprog) 857 858 p.As = AMOVD 859 p.From.Type = obj.TYPE_MEM 860 p.From.Reg = REGG 861 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 862 if c.cursym.CFunc() { 863 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 864 } 865 p.To.Type = obj.TYPE_REG 866 p.To.Reg = REG_R3 867 868 var q *obj.Prog 869 if framesize <= objabi.StackSmall { 870 // small stack: SP < stackguard 871 // CMP stackguard, SP 872 p = obj.Appendp(p, c.newprog) 873 874 p.As = ACMPU 875 p.From.Type = obj.TYPE_REG 876 p.From.Reg = REG_R3 877 p.To.Type = obj.TYPE_REG 878 p.To.Reg = REGSP 879 } else if framesize <= objabi.StackBig { 880 // large stack: SP-framesize < stackguard-StackSmall 881 // ADD $-(framesize-StackSmall), SP, R4 882 // CMP stackguard, R4 883 p = obj.Appendp(p, c.newprog) 884 885 p.As = AADD 886 p.From.Type = obj.TYPE_CONST 887 p.From.Offset = -(int64(framesize) - objabi.StackSmall) 888 p.Reg = REGSP 889 p.To.Type = obj.TYPE_REG 890 p.To.Reg = REG_R4 891 892 p = obj.Appendp(p, c.newprog) 893 p.As = ACMPU 894 p.From.Type = obj.TYPE_REG 895 p.From.Reg = REG_R3 896 p.To.Type = obj.TYPE_REG 897 p.To.Reg = REG_R4 898 } else { 899 // Such a large stack we need to protect against wraparound. 900 // If SP is close to zero: 901 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 902 // The +StackGuard on both sides is required to keep the left side positive: 903 // SP is allowed to be slightly below stackguard. See stack.h. 904 // 905 // Preemption sets stackguard to StackPreempt, a very large value. 906 // That breaks the math above, so we have to check for that explicitly. 907 // // stackguard is R3 908 // CMP R3, $StackPreempt 909 // BEQ label-of-call-to-morestack 910 // ADD $StackGuard, SP, R4 911 // SUB R3, R4 912 // MOVD $(framesize+(StackGuard-StackSmall)), R31 913 // CMPU R31, R4 914 p = obj.Appendp(p, c.newprog) 915 916 p.As = ACMP 917 p.From.Type = obj.TYPE_REG 918 p.From.Reg = REG_R3 919 p.To.Type = obj.TYPE_CONST 920 p.To.Offset = objabi.StackPreempt 921 922 p = obj.Appendp(p, c.newprog) 923 q = p 924 p.As = ABEQ 925 p.To.Type = obj.TYPE_BRANCH 926 927 p = obj.Appendp(p, c.newprog) 928 p.As = AADD 929 p.From.Type = obj.TYPE_CONST 930 p.From.Offset = objabi.StackGuard 931 p.Reg = REGSP 932 p.To.Type = obj.TYPE_REG 933 p.To.Reg = REG_R4 934 935 p = obj.Appendp(p, c.newprog) 936 p.As = ASUB 937 p.From.Type = obj.TYPE_REG 938 p.From.Reg = REG_R3 939 p.To.Type = obj.TYPE_REG 940 p.To.Reg = REG_R4 941 942 p = obj.Appendp(p, c.newprog) 943 p.As = AMOVD 944 p.From.Type = obj.TYPE_CONST 945 p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall 946 p.To.Type = obj.TYPE_REG 947 p.To.Reg = REGTMP 948 949 p = obj.Appendp(p, c.newprog) 950 p.As = ACMPU 951 p.From.Type = obj.TYPE_REG 952 p.From.Reg = REGTMP 953 p.To.Type = obj.TYPE_REG 954 p.To.Reg = REG_R4 955 } 956 957 // q1: BLT done 958 p = obj.Appendp(p, c.newprog) 959 q1 := p 960 961 p.As = ABLT 962 p.To.Type = obj.TYPE_BRANCH 963 964 // MOVD LR, R5 965 p = obj.Appendp(p, c.newprog) 966 967 p.As = AMOVD 968 p.From.Type = obj.TYPE_REG 969 p.From.Reg = REG_LR 970 p.To.Type = obj.TYPE_REG 971 p.To.Reg = REG_R5 972 if q != nil { 973 q.Pcond = p 974 } 975 976 p = c.ctxt.EmitEntryLiveness(c.cursym, p, c.newprog) 977 978 var morestacksym *obj.LSym 979 if c.cursym.CFunc() { 980 morestacksym = c.ctxt.Lookup("runtime.morestackc") 981 } else if !c.cursym.Func.Text.From.Sym.NeedCtxt() { 982 morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt") 983 } else { 984 morestacksym = c.ctxt.Lookup("runtime.morestack") 985 } 986 987 if c.ctxt.Flag_shared { 988 // In PPC64 PIC code, R2 is used as TOC pointer derived from R12 989 // which is the address of function entry point when entering 990 // the function. We need to preserve R2 across call to morestack. 991 // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in 992 // the caller's frame, but not used (0(SP) is caller's saved LR, 993 // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2. 994 995 // MOVD R12, 8(SP) 996 p = obj.Appendp(p, c.newprog) 997 p.As = AMOVD 998 p.From.Type = obj.TYPE_REG 999 p.From.Reg = REG_R2 1000 p.To.Type = obj.TYPE_MEM 1001 p.To.Reg = REGSP 1002 p.To.Offset = 8 1003 } 1004 1005 if c.ctxt.Flag_dynlink { 1006 // Avoid calling morestack via a PLT when dynamically linking. The 1007 // PLT stubs generated by the system linker on ppc64le when "std r2, 1008 // 24(r1)" to save the TOC pointer in their callers stack 1009 // frame. Unfortunately (and necessarily) morestack is called before 1010 // the function that calls it sets up its frame and so the PLT ends 1011 // up smashing the saved TOC pointer for its caller's caller. 1012 // 1013 // According to the ABI documentation there is a mechanism to avoid 1014 // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE 1015 // relocation on the nop after the call to morestack) but at the time 1016 // of writing it is not supported at all by gold and my attempt to 1017 // use it with ld.bfd caused an internal linker error. So this hack 1018 // seems preferable. 1019 1020 // MOVD $runtime.morestack(SB), R12 1021 p = obj.Appendp(p, c.newprog) 1022 p.As = AMOVD 1023 p.From.Type = obj.TYPE_MEM 1024 p.From.Sym = morestacksym 1025 p.From.Name = obj.NAME_GOTREF 1026 p.To.Type = obj.TYPE_REG 1027 p.To.Reg = REG_R12 1028 1029 // MOVD R12, CTR 1030 p = obj.Appendp(p, c.newprog) 1031 p.As = AMOVD 1032 p.From.Type = obj.TYPE_REG 1033 p.From.Reg = REG_R12 1034 p.To.Type = obj.TYPE_REG 1035 p.To.Reg = REG_CTR 1036 1037 // BL CTR 1038 p = obj.Appendp(p, c.newprog) 1039 p.As = obj.ACALL 1040 p.From.Type = obj.TYPE_REG 1041 p.From.Reg = REG_R12 1042 p.To.Type = obj.TYPE_REG 1043 p.To.Reg = REG_CTR 1044 } else { 1045 // BL runtime.morestack(SB) 1046 p = obj.Appendp(p, c.newprog) 1047 1048 p.As = ABL 1049 p.To.Type = obj.TYPE_BRANCH 1050 p.To.Sym = morestacksym 1051 } 1052 1053 if c.ctxt.Flag_shared { 1054 // MOVD 8(SP), R2 1055 p = obj.Appendp(p, c.newprog) 1056 p.As = AMOVD 1057 p.From.Type = obj.TYPE_MEM 1058 p.From.Reg = REGSP 1059 p.From.Offset = 8 1060 p.To.Type = obj.TYPE_REG 1061 p.To.Reg = REG_R2 1062 } 1063 1064 // BR start 1065 p = obj.Appendp(p, c.newprog) 1066 p.As = ABR 1067 p.To.Type = obj.TYPE_BRANCH 1068 p.Pcond = p0.Link 1069 1070 // placeholder for q1's jump target 1071 p = obj.Appendp(p, c.newprog) 1072 1073 p.As = obj.ANOP // zero-width place holder 1074 q1.Pcond = p 1075 1076 return p 1077 } 1078 1079 var Linkppc64 = obj.LinkArch{ 1080 Arch: sys.ArchPPC64, 1081 Init: buildop, 1082 Preprocess: preprocess, 1083 Assemble: span9, 1084 Progedit: progedit, 1085 DWARFRegisters: PPC64DWARFRegisters, 1086 } 1087 1088 var Linkppc64le = obj.LinkArch{ 1089 Arch: sys.ArchPPC64LE, 1090 Init: buildop, 1091 Preprocess: preprocess, 1092 Assemble: span9, 1093 Progedit: progedit, 1094 DWARFRegisters: PPC64DWARFRegisters, 1095 }