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