github.com/bir3/gocompiler@v0.3.205/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 "github.com/bir3/gocompiler/src/cmd/internal/obj" 34 "github.com/bir3/gocompiler/src/cmd/internal/objabi" 35 "github.com/bir3/gocompiler/src/cmd/internal/src" 36 "github.com/bir3/gocompiler/src/cmd/internal/sys" 37 "log" 38 ) 39 40 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 41 p.From.Class = 0 42 p.To.Class = 0 43 44 c := ctxt9{ctxt: ctxt, newprog: newprog} 45 46 // Rewrite BR/BL to symbol as TYPE_BRANCH. 47 switch p.As { 48 case ABR, 49 ABL, 50 obj.ARET, 51 obj.ADUFFZERO, 52 obj.ADUFFCOPY: 53 if p.To.Sym != nil { 54 p.To.Type = obj.TYPE_BRANCH 55 } 56 } 57 58 // Rewrite float constants to values stored in memory. 59 switch p.As { 60 case AFMOVS: 61 if p.From.Type == obj.TYPE_FCONST { 62 f32 := float32(p.From.Val.(float64)) 63 p.From.Type = obj.TYPE_MEM 64 p.From.Sym = ctxt.Float32Sym(f32) 65 p.From.Name = obj.NAME_EXTERN 66 p.From.Offset = 0 67 } 68 69 case AFMOVD: 70 if p.From.Type == obj.TYPE_FCONST { 71 f64 := p.From.Val.(float64) 72 // Constant not needed in memory for float +/- 0 73 if f64 != 0 { 74 p.From.Type = obj.TYPE_MEM 75 p.From.Sym = ctxt.Float64Sym(f64) 76 p.From.Name = obj.NAME_EXTERN 77 p.From.Offset = 0 78 } 79 } 80 81 case AMOVD: 82 // 32b constants (signed and unsigned) can be generated via 1 or 2 instructions. 83 // All others must be placed in memory and loaded. 84 isS32 := int64(int32(p.From.Offset)) == p.From.Offset 85 isU32 := uint64(uint32(p.From.Offset)) == uint64(p.From.Offset) 86 if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && !isS32 && !isU32 { 87 p.From.Type = obj.TYPE_MEM 88 p.From.Sym = ctxt.Int64Sym(p.From.Offset) 89 p.From.Name = obj.NAME_EXTERN 90 p.From.Offset = 0 91 } 92 } 93 94 switch p.As { 95 // Rewrite SUB constants into ADD. 96 case ASUBC: 97 if p.From.Type == obj.TYPE_CONST { 98 p.From.Offset = -p.From.Offset 99 p.As = AADDC 100 } 101 102 case ASUBCCC: 103 if p.From.Type == obj.TYPE_CONST { 104 p.From.Offset = -p.From.Offset 105 p.As = AADDCCC 106 } 107 108 case ASUB: 109 if p.From.Type == obj.TYPE_CONST { 110 p.From.Offset = -p.From.Offset 111 p.As = AADD 112 } 113 114 // To maintain backwards compatibility, we accept some 4 argument usage of 115 // several opcodes which was likely not intended, but did work. These are not 116 // added to optab to avoid the chance this behavior might be used with newer 117 // instructions. 118 // 119 // Rewrite argument ordering like "ADDEX R3, $3, R4, R5" into 120 // "ADDEX R3, R4, $3, R5" 121 case AVSHASIGMAW, AVSHASIGMAD, AADDEX, AXXSLDWI, AXXPERMDI: 122 if len(p.RestArgs) == 2 && p.Reg == 0 && p.RestArgs[0].Addr.Type == obj.TYPE_CONST && p.RestArgs[1].Addr.Type == obj.TYPE_REG { 123 p.Reg = p.RestArgs[1].Addr.Reg 124 p.RestArgs = p.RestArgs[:1] 125 } 126 } 127 128 if c.ctxt.Headtype == objabi.Haix { 129 c.rewriteToUseTOC(p) 130 } else if c.ctxt.Flag_dynlink { 131 c.rewriteToUseGot(p) 132 } 133 } 134 135 // Rewrite p, if necessary, to access a symbol using its TOC anchor. 136 // This code is for AIX only. 137 func (c *ctxt9) rewriteToUseTOC(p *obj.Prog) { 138 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 139 return 140 } 141 142 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 143 // ADUFFZERO/ADUFFCOPY is considered as an ABL except in dynamic 144 // link where it should be an indirect call. 145 if !c.ctxt.Flag_dynlink { 146 return 147 } 148 // ADUFFxxx $offset 149 // becomes 150 // MOVD runtime.duffxxx@TOC, R12 151 // ADD $offset, R12 152 // MOVD R12, LR 153 // BL (LR) 154 var sym *obj.LSym 155 if p.As == obj.ADUFFZERO { 156 sym = c.ctxt.Lookup("runtime.duffzero") 157 } else { 158 sym = c.ctxt.Lookup("runtime.duffcopy") 159 } 160 // Retrieve or create the TOC anchor. 161 symtoc := c.ctxt.LookupInit("TOC."+sym.Name, func(s *obj.LSym) { 162 s.Type = objabi.SDATA 163 s.Set(obj.AttrDuplicateOK, true) 164 s.Set(obj.AttrStatic, true) 165 c.ctxt.Data = append(c.ctxt.Data, s) 166 s.WriteAddr(c.ctxt, 0, 8, sym, 0) 167 }) 168 169 offset := p.To.Offset 170 p.As = AMOVD 171 p.From.Type = obj.TYPE_MEM 172 p.From.Name = obj.NAME_TOCREF 173 p.From.Sym = symtoc 174 p.To.Type = obj.TYPE_REG 175 p.To.Reg = REG_R12 176 p.To.Name = obj.NAME_NONE 177 p.To.Offset = 0 178 p.To.Sym = nil 179 p1 := obj.Appendp(p, c.newprog) 180 p1.As = AADD 181 p1.From.Type = obj.TYPE_CONST 182 p1.From.Offset = offset 183 p1.To.Type = obj.TYPE_REG 184 p1.To.Reg = REG_R12 185 p2 := obj.Appendp(p1, c.newprog) 186 p2.As = AMOVD 187 p2.From.Type = obj.TYPE_REG 188 p2.From.Reg = REG_R12 189 p2.To.Type = obj.TYPE_REG 190 p2.To.Reg = REG_LR 191 p3 := obj.Appendp(p2, c.newprog) 192 p3.As = obj.ACALL 193 p3.To.Type = obj.TYPE_REG 194 p3.To.Reg = REG_LR 195 } 196 197 var source *obj.Addr 198 if p.From.Name == obj.NAME_EXTERN || p.From.Name == obj.NAME_STATIC { 199 if p.From.Type == obj.TYPE_ADDR { 200 if p.As == ADWORD { 201 // ADWORD $sym doesn't need TOC anchor 202 return 203 } 204 if p.As != AMOVD { 205 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v", p) 206 return 207 } 208 if p.To.Type != obj.TYPE_REG { 209 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v", p) 210 return 211 } 212 } else if p.From.Type != obj.TYPE_MEM { 213 c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p) 214 return 215 } 216 source = &p.From 217 218 } else if p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC { 219 if p.To.Type != obj.TYPE_MEM { 220 c.ctxt.Diag("do not know how to handle %v without TYPE_MEM", p) 221 return 222 } 223 if source != nil { 224 c.ctxt.Diag("cannot handle symbols on both sides in %v", p) 225 return 226 } 227 source = &p.To 228 } else { 229 return 230 231 } 232 233 if source.Sym == nil { 234 c.ctxt.Diag("do not know how to handle nil symbol in %v", p) 235 return 236 } 237 238 if source.Sym.Type == objabi.STLSBSS { 239 return 240 } 241 242 // Retrieve or create the TOC anchor. 243 symtoc := c.ctxt.LookupInit("TOC."+source.Sym.Name, func(s *obj.LSym) { 244 s.Type = objabi.SDATA 245 s.Set(obj.AttrDuplicateOK, true) 246 s.Set(obj.AttrStatic, true) 247 c.ctxt.Data = append(c.ctxt.Data, s) 248 s.WriteAddr(c.ctxt, 0, 8, source.Sym, 0) 249 }) 250 251 if source.Type == obj.TYPE_ADDR { 252 // MOVD $sym, Rx becomes MOVD symtoc, Rx 253 // MOVD $sym+<off>, Rx becomes MOVD symtoc, Rx; ADD <off>, Rx 254 p.From.Type = obj.TYPE_MEM 255 p.From.Sym = symtoc 256 p.From.Name = obj.NAME_TOCREF 257 258 if p.From.Offset != 0 { 259 q := obj.Appendp(p, c.newprog) 260 q.As = AADD 261 q.From.Type = obj.TYPE_CONST 262 q.From.Offset = p.From.Offset 263 p.From.Offset = 0 264 q.To = p.To 265 } 266 return 267 268 } 269 270 // MOVx sym, Ry becomes MOVD symtoc, REGTMP; MOVx (REGTMP), Ry 271 // MOVx Ry, sym becomes MOVD symtoc, REGTMP; MOVx Ry, (REGTMP) 272 // An addition may be inserted between the two MOVs if there is an offset. 273 274 q := obj.Appendp(p, c.newprog) 275 q.As = AMOVD 276 q.From.Type = obj.TYPE_MEM 277 q.From.Sym = symtoc 278 q.From.Name = obj.NAME_TOCREF 279 q.To.Type = obj.TYPE_REG 280 q.To.Reg = REGTMP 281 282 q = obj.Appendp(q, c.newprog) 283 q.As = p.As 284 q.From = p.From 285 q.To = p.To 286 if p.From.Name != obj.NAME_NONE { 287 q.From.Type = obj.TYPE_MEM 288 q.From.Reg = REGTMP 289 q.From.Name = obj.NAME_NONE 290 q.From.Sym = nil 291 } else if p.To.Name != obj.NAME_NONE { 292 q.To.Type = obj.TYPE_MEM 293 q.To.Reg = REGTMP 294 q.To.Name = obj.NAME_NONE 295 q.To.Sym = nil 296 } else { 297 c.ctxt.Diag("unreachable case in rewriteToUseTOC with %v", p) 298 } 299 300 obj.Nopout(p) 301 } 302 303 // Rewrite p, if necessary, to access global data via the global offset table. 304 func (c *ctxt9) rewriteToUseGot(p *obj.Prog) { 305 if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO { 306 // ADUFFxxx $offset 307 // becomes 308 // MOVD runtime.duffxxx@GOT, R12 309 // ADD $offset, R12 310 // MOVD R12, LR 311 // BL (LR) 312 var sym *obj.LSym 313 if p.As == obj.ADUFFZERO { 314 sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal) 315 } else { 316 sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal) 317 } 318 offset := p.To.Offset 319 p.As = AMOVD 320 p.From.Type = obj.TYPE_MEM 321 p.From.Name = obj.NAME_GOTREF 322 p.From.Sym = sym 323 p.To.Type = obj.TYPE_REG 324 p.To.Reg = REG_R12 325 p.To.Name = obj.NAME_NONE 326 p.To.Offset = 0 327 p.To.Sym = nil 328 p1 := obj.Appendp(p, c.newprog) 329 p1.As = AADD 330 p1.From.Type = obj.TYPE_CONST 331 p1.From.Offset = offset 332 p1.To.Type = obj.TYPE_REG 333 p1.To.Reg = REG_R12 334 p2 := obj.Appendp(p1, c.newprog) 335 p2.As = AMOVD 336 p2.From.Type = obj.TYPE_REG 337 p2.From.Reg = REG_R12 338 p2.To.Type = obj.TYPE_REG 339 p2.To.Reg = REG_LR 340 p3 := obj.Appendp(p2, c.newprog) 341 p3.As = obj.ACALL 342 p3.To.Type = obj.TYPE_REG 343 p3.To.Reg = REG_LR 344 } 345 346 // We only care about global data: NAME_EXTERN means a global 347 // symbol in the Go sense, and p.Sym.Local is true for a few 348 // internally defined symbols. 349 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 350 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 351 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx 352 if p.As != AMOVD { 353 c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p) 354 } 355 if p.To.Type != obj.TYPE_REG { 356 c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p) 357 } 358 p.From.Type = obj.TYPE_MEM 359 p.From.Name = obj.NAME_GOTREF 360 if p.From.Offset != 0 { 361 q := obj.Appendp(p, c.newprog) 362 q.As = AADD 363 q.From.Type = obj.TYPE_CONST 364 q.From.Offset = p.From.Offset 365 q.To = p.To 366 p.From.Offset = 0 367 } 368 } 369 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 370 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 371 } 372 var source *obj.Addr 373 // MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry 374 // MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVx Ry, (REGTMP) 375 // An addition may be inserted between the two MOVs if there is an offset. 376 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 377 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 378 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 379 } 380 source = &p.From 381 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 382 source = &p.To 383 } else { 384 return 385 } 386 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 387 return 388 } 389 if source.Sym.Type == objabi.STLSBSS { 390 return 391 } 392 if source.Type != obj.TYPE_MEM { 393 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 394 } 395 p1 := obj.Appendp(p, c.newprog) 396 p2 := obj.Appendp(p1, c.newprog) 397 398 p1.As = AMOVD 399 p1.From.Type = obj.TYPE_MEM 400 p1.From.Sym = source.Sym 401 p1.From.Name = obj.NAME_GOTREF 402 p1.To.Type = obj.TYPE_REG 403 p1.To.Reg = REGTMP 404 405 p2.As = p.As 406 p2.From = p.From 407 p2.To = p.To 408 if p.From.Name == obj.NAME_EXTERN { 409 p2.From.Reg = REGTMP 410 p2.From.Name = obj.NAME_NONE 411 p2.From.Sym = nil 412 } else if p.To.Name == obj.NAME_EXTERN { 413 p2.To.Reg = REGTMP 414 p2.To.Name = obj.NAME_NONE 415 p2.To.Sym = nil 416 } else { 417 return 418 } 419 obj.Nopout(p) 420 } 421 422 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 423 // TODO(minux): add morestack short-cuts with small fixed frame-size. 424 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { 425 return 426 } 427 428 c := ctxt9{ctxt: ctxt, cursym: cursym, newprog: newprog} 429 430 p := c.cursym.Func().Text 431 textstksiz := p.To.Offset 432 if textstksiz == -8 { 433 // Compatibility hack. 434 p.From.Sym.Set(obj.AttrNoFrame, true) 435 textstksiz = 0 436 } 437 if textstksiz%8 != 0 { 438 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz) 439 } 440 if p.From.Sym.NoFrame() { 441 if textstksiz != 0 { 442 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 443 } 444 } 445 446 c.cursym.Func().Args = p.To.Val.(int32) 447 c.cursym.Func().Locals = int32(textstksiz) 448 449 /* 450 * find leaf subroutines 451 * expand RET 452 * expand BECOME pseudo 453 */ 454 455 var q *obj.Prog 456 var q1 *obj.Prog 457 for p := c.cursym.Func().Text; p != nil; p = p.Link { 458 switch p.As { 459 /* too hard, just leave alone */ 460 case obj.ATEXT: 461 q = p 462 463 p.Mark |= LABEL | LEAF | SYNC 464 if p.Link != nil { 465 p.Link.Mark |= LABEL 466 } 467 468 case ANOR: 469 q = p 470 if p.To.Type == obj.TYPE_REG { 471 if p.To.Reg == REGZERO { 472 p.Mark |= LABEL | SYNC 473 } 474 } 475 476 case ALWAR, 477 ALBAR, 478 ASTBCCC, 479 ASTWCCC, 480 AEIEIO, 481 AICBI, 482 AISYNC, 483 ATLBIE, 484 ATLBIEL, 485 ASLBIA, 486 ASLBIE, 487 ASLBMFEE, 488 ASLBMFEV, 489 ASLBMTE, 490 ADCBF, 491 ADCBI, 492 ADCBST, 493 ADCBT, 494 ADCBTST, 495 ADCBZ, 496 ASYNC, 497 ATLBSYNC, 498 APTESYNC, 499 ALWSYNC, 500 ATW, 501 AWORD, 502 ARFI, 503 ARFCI, 504 ARFID, 505 AHRFID: 506 q = p 507 p.Mark |= LABEL | SYNC 508 continue 509 510 case AMOVW, AMOVWZ, AMOVD: 511 q = p 512 if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { 513 p.Mark |= LABEL | SYNC 514 } 515 continue 516 517 case AFABS, 518 AFABSCC, 519 AFADD, 520 AFADDCC, 521 AFCTIW, 522 AFCTIWCC, 523 AFCTIWZ, 524 AFCTIWZCC, 525 AFDIV, 526 AFDIVCC, 527 AFMADD, 528 AFMADDCC, 529 AFMOVD, 530 AFMOVDU, 531 /* case AFMOVDS: */ 532 AFMOVS, 533 AFMOVSU, 534 535 /* case AFMOVSD: */ 536 AFMSUB, 537 AFMSUBCC, 538 AFMUL, 539 AFMULCC, 540 AFNABS, 541 AFNABSCC, 542 AFNEG, 543 AFNEGCC, 544 AFNMADD, 545 AFNMADDCC, 546 AFNMSUB, 547 AFNMSUBCC, 548 AFRSP, 549 AFRSPCC, 550 AFSUB, 551 AFSUBCC: 552 q = p 553 554 p.Mark |= FLOAT 555 continue 556 557 case ABL, 558 ABCL, 559 obj.ADUFFZERO, 560 obj.ADUFFCOPY: 561 c.cursym.Func().Text.Mark &^= LEAF 562 fallthrough 563 564 case ABC, 565 ABEQ, 566 ABGE, 567 ABGT, 568 ABLE, 569 ABLT, 570 ABNE, 571 ABR, 572 ABVC, 573 ABVS: 574 p.Mark |= BRANCH 575 q = p 576 q1 = p.To.Target() 577 if q1 != nil { 578 // NOPs are not removed due to #40689. 579 580 if q1.Mark&LEAF == 0 { 581 q1.Mark |= LABEL 582 } 583 } else { 584 p.Mark |= LABEL 585 } 586 q1 = p.Link 587 if q1 != nil { 588 q1.Mark |= LABEL 589 } 590 continue 591 592 case AFCMPO, AFCMPU: 593 q = p 594 p.Mark |= FCMP | FLOAT 595 continue 596 597 case obj.ARET: 598 q = p 599 if p.Link != nil { 600 p.Link.Mark |= LABEL 601 } 602 continue 603 604 case obj.ANOP: 605 // NOPs are not removed due to 606 // #40689 607 continue 608 609 default: 610 q = p 611 continue 612 } 613 } 614 615 autosize := int32(0) 616 var p1 *obj.Prog 617 var p2 *obj.Prog 618 for p := c.cursym.Func().Text; p != nil; p = p.Link { 619 o := p.As 620 switch o { 621 case obj.ATEXT: 622 autosize = int32(textstksiz) 623 624 if p.Mark&LEAF != 0 && autosize == 0 { 625 // A leaf function with no locals has no frame. 626 p.From.Sym.Set(obj.AttrNoFrame, true) 627 } 628 629 if !p.From.Sym.NoFrame() { 630 // If there is a stack frame at all, it includes 631 // space to save the LR. 632 autosize += int32(c.ctxt.Arch.FixedFrameSize) 633 } 634 635 if p.Mark&LEAF != 0 && autosize < objabi.StackSmall { 636 // A leaf function with a small stack can be marked 637 // NOSPLIT, avoiding a stack check. 638 p.From.Sym.Set(obj.AttrNoSplit, true) 639 } 640 641 p.To.Offset = int64(autosize) 642 643 q = p 644 645 if c.ctxt.Flag_shared && c.cursym.Name != "runtime.duffzero" && c.cursym.Name != "runtime.duffcopy" { 646 // When compiling Go into PIC, all functions must start 647 // with instructions to load the TOC pointer into r2: 648 // 649 // addis r2, r12, .TOC.-func@ha 650 // addi r2, r2, .TOC.-func@l+4 651 // 652 // We could probably skip this prologue in some situations 653 // but it's a bit subtle. However, it is both safe and 654 // necessary to leave the prologue off duffzero and 655 // duffcopy as we rely on being able to jump to a specific 656 // instruction offset for them. 657 // 658 // These are AWORDS because there is no (afaict) way to 659 // generate the addis instruction except as part of the 660 // load of a large constant, and in that case there is no 661 // way to use r12 as the source. 662 // 663 // Note that the same condition is tested in 664 // putelfsym in cmd/link/internal/ld/symtab.go 665 // where we set the st_other field to indicate 666 // the presence of these instructions. 667 q = obj.Appendp(q, c.newprog) 668 q.As = AWORD 669 q.Pos = p.Pos 670 q.From.Type = obj.TYPE_CONST 671 q.From.Offset = 0x3c4c0000 672 q = obj.Appendp(q, c.newprog) 673 q.As = AWORD 674 q.Pos = p.Pos 675 q.From.Type = obj.TYPE_CONST 676 q.From.Offset = 0x38420000 677 rel := obj.Addrel(c.cursym) 678 rel.Off = 0 679 rel.Siz = 8 680 rel.Sym = c.ctxt.Lookup(".TOC.") 681 rel.Type = objabi.R_ADDRPOWER_PCREL 682 } 683 684 if !c.cursym.Func().Text.From.Sym.NoSplit() { 685 q = c.stacksplit(q, autosize) // emit split check 686 } 687 688 // Special handling of the racecall thunk. Assume that its asm code will 689 // save the link register and update the stack, since that code is 690 // called directly from C/C++ and can't clobber REGTMP (R31). 691 if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { 692 var prologueEnd *obj.Prog 693 // Save the link register and update the SP. MOVDU is used unless 694 // the frame size is too large. The link register must be saved 695 // even for non-empty leaf functions so that traceback works. 696 if autosize >= -BIG && autosize <= BIG { 697 // Use MOVDU to adjust R1 when saving R31, if autosize is small. 698 q = obj.Appendp(q, c.newprog) 699 q.As = AMOVD 700 q.Pos = p.Pos 701 q.From.Type = obj.TYPE_REG 702 q.From.Reg = REG_LR 703 q.To.Type = obj.TYPE_REG 704 q.To.Reg = REGTMP 705 prologueEnd = q 706 707 q = obj.Appendp(q, c.newprog) 708 q.As = AMOVDU 709 q.Pos = p.Pos 710 q.From.Type = obj.TYPE_REG 711 q.From.Reg = REGTMP 712 q.To.Type = obj.TYPE_MEM 713 q.To.Offset = int64(-autosize) 714 q.To.Reg = REGSP 715 q.Spadj = autosize 716 } else { 717 // Frame size is too large for a MOVDU instruction. 718 // Store link register before decrementing SP, so if a signal comes 719 // during the execution of the function prologue, the traceback 720 // code will not see a half-updated stack frame. 721 // This sequence is not async preemptible, as if we open a frame 722 // at the current SP, it will clobber the saved LR. 723 q = obj.Appendp(q, c.newprog) 724 q.As = AMOVD 725 q.Pos = p.Pos 726 q.From.Type = obj.TYPE_REG 727 q.From.Reg = REG_LR 728 q.To.Type = obj.TYPE_REG 729 q.To.Reg = REG_R29 // REGTMP may be used to synthesize large offset in the next instruction 730 731 q = c.ctxt.StartUnsafePoint(q, c.newprog) 732 733 q = obj.Appendp(q, c.newprog) 734 q.As = AMOVD 735 q.Pos = p.Pos 736 q.From.Type = obj.TYPE_REG 737 q.From.Reg = REG_R29 738 q.To.Type = obj.TYPE_MEM 739 q.To.Offset = int64(-autosize) 740 q.To.Reg = REGSP 741 742 prologueEnd = q 743 744 q = obj.Appendp(q, c.newprog) 745 q.As = AADD 746 q.Pos = p.Pos 747 q.From.Type = obj.TYPE_CONST 748 q.From.Offset = int64(-autosize) 749 q.To.Type = obj.TYPE_REG 750 q.To.Reg = REGSP 751 q.Spadj = +autosize 752 753 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) 754 } 755 prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd) 756 } else if c.cursym.Func().Text.Mark&LEAF == 0 { 757 // A very few functions that do not return to their caller 758 // (e.g. gogo) are not identified as leaves but still have 759 // no frame. 760 c.cursym.Func().Text.Mark |= LEAF 761 } 762 763 if c.cursym.Func().Text.Mark&LEAF != 0 { 764 c.cursym.Set(obj.AttrLeaf, true) 765 break 766 } 767 768 if c.ctxt.Flag_shared { 769 q = obj.Appendp(q, c.newprog) 770 q.As = AMOVD 771 q.Pos = p.Pos 772 q.From.Type = obj.TYPE_REG 773 q.From.Reg = REG_R2 774 q.To.Type = obj.TYPE_MEM 775 q.To.Reg = REGSP 776 q.To.Offset = 24 777 } 778 779 if c.cursym.Func().Text.From.Sym.Wrapper() { 780 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 781 // 782 // MOVD g_panic(g), R3 783 // CMP R0, R3 784 // BEQ end 785 // MOVD panic_argp(R3), R4 786 // ADD $(autosize+8), R1, R5 787 // CMP R4, R5 788 // BNE end 789 // ADD $8, R1, R6 790 // MOVD R6, panic_argp(R3) 791 // end: 792 // NOP 793 // 794 // The NOP is needed to give the jumps somewhere to land. 795 // It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes. 796 797 q = obj.Appendp(q, c.newprog) 798 799 q.As = AMOVD 800 q.From.Type = obj.TYPE_MEM 801 q.From.Reg = REGG 802 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 803 q.To.Type = obj.TYPE_REG 804 q.To.Reg = REG_R22 805 806 q = obj.Appendp(q, c.newprog) 807 q.As = ACMP 808 q.From.Type = obj.TYPE_REG 809 q.From.Reg = REG_R0 810 q.To.Type = obj.TYPE_REG 811 q.To.Reg = REG_R22 812 813 q = obj.Appendp(q, c.newprog) 814 q.As = ABEQ 815 q.To.Type = obj.TYPE_BRANCH 816 p1 = q 817 818 q = obj.Appendp(q, c.newprog) 819 q.As = AMOVD 820 q.From.Type = obj.TYPE_MEM 821 q.From.Reg = REG_R22 822 q.From.Offset = 0 // Panic.argp 823 q.To.Type = obj.TYPE_REG 824 q.To.Reg = REG_R23 825 826 q = obj.Appendp(q, c.newprog) 827 q.As = AADD 828 q.From.Type = obj.TYPE_CONST 829 q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize 830 q.Reg = REGSP 831 q.To.Type = obj.TYPE_REG 832 q.To.Reg = REG_R24 833 834 q = obj.Appendp(q, c.newprog) 835 q.As = ACMP 836 q.From.Type = obj.TYPE_REG 837 q.From.Reg = REG_R23 838 q.To.Type = obj.TYPE_REG 839 q.To.Reg = REG_R24 840 841 q = obj.Appendp(q, c.newprog) 842 q.As = ABNE 843 q.To.Type = obj.TYPE_BRANCH 844 p2 = q 845 846 q = obj.Appendp(q, c.newprog) 847 q.As = AADD 848 q.From.Type = obj.TYPE_CONST 849 q.From.Offset = c.ctxt.Arch.FixedFrameSize 850 q.Reg = REGSP 851 q.To.Type = obj.TYPE_REG 852 q.To.Reg = REG_R25 853 854 q = obj.Appendp(q, c.newprog) 855 q.As = AMOVD 856 q.From.Type = obj.TYPE_REG 857 q.From.Reg = REG_R25 858 q.To.Type = obj.TYPE_MEM 859 q.To.Reg = REG_R22 860 q.To.Offset = 0 // Panic.argp 861 862 q = obj.Appendp(q, c.newprog) 863 864 q.As = obj.ANOP 865 p1.To.SetTarget(q) 866 p2.To.SetTarget(q) 867 } 868 869 case obj.ARET: 870 if p.From.Type == obj.TYPE_CONST { 871 c.ctxt.Diag("using BECOME (%v) is not supported!", p) 872 break 873 } 874 875 retTarget := p.To.Sym 876 877 if c.cursym.Func().Text.Mark&LEAF != 0 { 878 if autosize == 0 || c.cursym.Name == "runtime.racecallbackthunk" { 879 p.As = ABR 880 p.From = obj.Addr{} 881 if retTarget == nil { 882 p.To.Type = obj.TYPE_REG 883 p.To.Reg = REG_LR 884 } else { 885 p.To.Type = obj.TYPE_BRANCH 886 p.To.Sym = retTarget 887 } 888 p.Mark |= BRANCH 889 break 890 } 891 892 p.As = AADD 893 p.From.Type = obj.TYPE_CONST 894 p.From.Offset = int64(autosize) 895 p.To.Type = obj.TYPE_REG 896 p.To.Reg = REGSP 897 p.Spadj = -autosize 898 899 q = c.newprog() 900 q.As = ABR 901 q.Pos = p.Pos 902 if retTarget == nil { 903 q.To.Type = obj.TYPE_REG 904 q.To.Reg = REG_LR 905 } else { 906 q.To.Type = obj.TYPE_BRANCH 907 q.To.Sym = retTarget 908 } 909 q.Mark |= BRANCH 910 q.Spadj = +autosize 911 912 q.Link = p.Link 913 p.Link = q 914 break 915 } 916 917 p.As = AMOVD 918 p.From.Type = obj.TYPE_MEM 919 p.From.Offset = 0 920 p.From.Reg = REGSP 921 p.To.Type = obj.TYPE_REG 922 p.To.Reg = REGTMP 923 924 q = c.newprog() 925 q.As = AMOVD 926 q.Pos = p.Pos 927 q.From.Type = obj.TYPE_REG 928 q.From.Reg = REGTMP 929 q.To.Type = obj.TYPE_REG 930 q.To.Reg = REG_LR 931 932 q.Link = p.Link 933 p.Link = q 934 p = q 935 936 if false { 937 // Debug bad returns 938 q = c.newprog() 939 940 q.As = AMOVD 941 q.Pos = p.Pos 942 q.From.Type = obj.TYPE_MEM 943 q.From.Offset = 0 944 q.From.Reg = REGTMP 945 q.To.Type = obj.TYPE_REG 946 q.To.Reg = REGTMP 947 948 q.Link = p.Link 949 p.Link = q 950 p = q 951 } 952 prev := p 953 if autosize != 0 && c.cursym.Name != "runtime.racecallbackthunk" { 954 q = c.newprog() 955 q.As = AADD 956 q.Pos = p.Pos 957 q.From.Type = obj.TYPE_CONST 958 q.From.Offset = int64(autosize) 959 q.To.Type = obj.TYPE_REG 960 q.To.Reg = REGSP 961 q.Spadj = -autosize 962 963 q.Link = p.Link 964 prev.Link = q 965 prev = q 966 } 967 968 q1 = c.newprog() 969 q1.As = ABR 970 q1.Pos = p.Pos 971 if retTarget == nil { 972 q1.To.Type = obj.TYPE_REG 973 q1.To.Reg = REG_LR 974 } else { 975 q1.To.Type = obj.TYPE_BRANCH 976 q1.To.Sym = retTarget 977 } 978 q1.Mark |= BRANCH 979 q1.Spadj = +autosize 980 981 q1.Link = q.Link 982 prev.Link = q1 983 case AADD: 984 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 985 p.Spadj = int32(-p.From.Offset) 986 } 987 case AMOVDU: 988 if p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP { 989 p.Spadj = int32(-p.To.Offset) 990 } 991 if p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP { 992 p.Spadj = int32(-p.From.Offset) 993 } 994 case obj.AGETCALLERPC: 995 if cursym.Leaf() { 996 /* MOVD LR, Rd */ 997 p.As = AMOVD 998 p.From.Type = obj.TYPE_REG 999 p.From.Reg = REG_LR 1000 } else { 1001 /* MOVD (RSP), Rd */ 1002 p.As = AMOVD 1003 p.From.Type = obj.TYPE_MEM 1004 p.From.Reg = REGSP 1005 } 1006 } 1007 1008 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 && p.As != ACMPU { 1009 f := c.cursym.Func() 1010 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 { 1011 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE 1012 if ctxt.Debugvlog || !ctxt.IsAsm { 1013 ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p) 1014 if !ctxt.IsAsm { 1015 ctxt.Diag("invalid auto-SPWRITE in non-assembly") 1016 ctxt.DiagFlush() 1017 log.Fatalf("bad SPWRITE") 1018 } 1019 } 1020 } 1021 } 1022 } 1023 } 1024 1025 /* 1026 // instruction scheduling 1027 1028 if(debug['Q'] == 0) 1029 return; 1030 1031 curtext = nil; 1032 q = nil; // p - 1 1033 q1 = firstp; // top of block 1034 o = 0; // count of instructions 1035 for(p = firstp; p != nil; p = p1) { 1036 p1 = p->link; 1037 o++; 1038 if(p->mark & NOSCHED){ 1039 if(q1 != p){ 1040 sched(q1, q); 1041 } 1042 for(; p != nil; p = p->link){ 1043 if(!(p->mark & NOSCHED)) 1044 break; 1045 q = p; 1046 } 1047 p1 = p; 1048 q1 = p; 1049 o = 0; 1050 continue; 1051 } 1052 if(p->mark & (LABEL|SYNC)) { 1053 if(q1 != p) 1054 sched(q1, q); 1055 q1 = p; 1056 o = 1; 1057 } 1058 if(p->mark & (BRANCH|SYNC)) { 1059 sched(q1, p); 1060 q1 = p1; 1061 o = 0; 1062 } 1063 if(o >= NSCHED) { 1064 sched(q1, p); 1065 q1 = p1; 1066 o = 0; 1067 } 1068 q = p; 1069 } 1070 */ 1071 func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog { 1072 if c.ctxt.Flag_maymorestack != "" { 1073 if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink { 1074 // See the call to morestack for why these are 1075 // complicated to support. 1076 c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported") 1077 } 1078 1079 // Spill arguments. This has to happen before we open 1080 // any more frame space. 1081 p = c.cursym.Func().SpillRegisterArgs(p, c.newprog) 1082 1083 // Save LR and REGCTXT 1084 frameSize := 8 + c.ctxt.Arch.FixedFrameSize 1085 1086 // MOVD LR, REGTMP 1087 p = obj.Appendp(p, c.newprog) 1088 p.As = AMOVD 1089 p.From.Type = obj.TYPE_REG 1090 p.From.Reg = REG_LR 1091 p.To.Type = obj.TYPE_REG 1092 p.To.Reg = REGTMP 1093 // MOVDU REGTMP, -16(SP) 1094 p = obj.Appendp(p, c.newprog) 1095 p.As = AMOVDU 1096 p.From.Type = obj.TYPE_REG 1097 p.From.Reg = REGTMP 1098 p.To.Type = obj.TYPE_MEM 1099 p.To.Offset = -frameSize 1100 p.To.Reg = REGSP 1101 p.Spadj = int32(frameSize) 1102 1103 // MOVD REGCTXT, 8(SP) 1104 p = obj.Appendp(p, c.newprog) 1105 p.As = AMOVD 1106 p.From.Type = obj.TYPE_REG 1107 p.From.Reg = REGCTXT 1108 p.To.Type = obj.TYPE_MEM 1109 p.To.Offset = 8 1110 p.To.Reg = REGSP 1111 1112 // BL maymorestack 1113 p = obj.Appendp(p, c.newprog) 1114 p.As = ABL 1115 p.To.Type = obj.TYPE_BRANCH 1116 // See ../x86/obj6.go 1117 p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI()) 1118 1119 // Restore LR and REGCTXT 1120 1121 // MOVD 8(SP), REGCTXT 1122 p = obj.Appendp(p, c.newprog) 1123 p.As = AMOVD 1124 p.From.Type = obj.TYPE_MEM 1125 p.From.Offset = 8 1126 p.From.Reg = REGSP 1127 p.To.Type = obj.TYPE_REG 1128 p.To.Reg = REGCTXT 1129 1130 // MOVD 0(SP), REGTMP 1131 p = obj.Appendp(p, c.newprog) 1132 p.As = AMOVD 1133 p.From.Type = obj.TYPE_MEM 1134 p.From.Offset = 0 1135 p.From.Reg = REGSP 1136 p.To.Type = obj.TYPE_REG 1137 p.To.Reg = REGTMP 1138 1139 // MOVD REGTMP, LR 1140 p = obj.Appendp(p, c.newprog) 1141 p.As = AMOVD 1142 p.From.Type = obj.TYPE_REG 1143 p.From.Reg = REGTMP 1144 p.To.Type = obj.TYPE_REG 1145 p.To.Reg = REG_LR 1146 1147 // ADD $16, SP 1148 p = obj.Appendp(p, c.newprog) 1149 p.As = AADD 1150 p.From.Type = obj.TYPE_CONST 1151 p.From.Offset = frameSize 1152 p.To.Type = obj.TYPE_REG 1153 p.To.Reg = REGSP 1154 p.Spadj = -int32(frameSize) 1155 1156 // Unspill arguments. 1157 p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog) 1158 } 1159 1160 // save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack 1161 startPred := p 1162 1163 // MOVD g_stackguard(g), R22 1164 p = obj.Appendp(p, c.newprog) 1165 1166 p.As = AMOVD 1167 p.From.Type = obj.TYPE_MEM 1168 p.From.Reg = REGG 1169 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 1170 if c.cursym.CFunc() { 1171 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 1172 } 1173 p.To.Type = obj.TYPE_REG 1174 p.To.Reg = REG_R22 1175 1176 // Mark the stack bound check and morestack call async nonpreemptible. 1177 // If we get preempted here, when resumed the preemption request is 1178 // cleared, but we'll still call morestack, which will double the stack 1179 // unnecessarily. See issue #35470. 1180 p = c.ctxt.StartUnsafePoint(p, c.newprog) 1181 1182 var q *obj.Prog 1183 if framesize <= objabi.StackSmall { 1184 // small stack: SP < stackguard 1185 // CMP stackguard, SP 1186 p = obj.Appendp(p, c.newprog) 1187 1188 p.As = ACMPU 1189 p.From.Type = obj.TYPE_REG 1190 p.From.Reg = REG_R22 1191 p.To.Type = obj.TYPE_REG 1192 p.To.Reg = REGSP 1193 } else { 1194 // large stack: SP-framesize < stackguard-StackSmall 1195 offset := int64(framesize) - objabi.StackSmall 1196 if framesize > objabi.StackBig { 1197 // Such a large stack we need to protect against underflow. 1198 // The runtime guarantees SP > objabi.StackBig, but 1199 // framesize is large enough that SP-framesize may 1200 // underflow, causing a direct comparison with the 1201 // stack guard to incorrectly succeed. We explicitly 1202 // guard against underflow. 1203 // 1204 // CMPU SP, $(framesize-StackSmall) 1205 // BLT label-of-call-to-morestack 1206 if offset <= 0xffff { 1207 p = obj.Appendp(p, c.newprog) 1208 p.As = ACMPU 1209 p.From.Type = obj.TYPE_REG 1210 p.From.Reg = REGSP 1211 p.To.Type = obj.TYPE_CONST 1212 p.To.Offset = offset 1213 } else { 1214 // Constant is too big for CMPU. 1215 p = obj.Appendp(p, c.newprog) 1216 p.As = AMOVD 1217 p.From.Type = obj.TYPE_CONST 1218 p.From.Offset = offset 1219 p.To.Type = obj.TYPE_REG 1220 p.To.Reg = REG_R23 1221 1222 p = obj.Appendp(p, c.newprog) 1223 p.As = ACMPU 1224 p.From.Type = obj.TYPE_REG 1225 p.From.Reg = REGSP 1226 p.To.Type = obj.TYPE_REG 1227 p.To.Reg = REG_R23 1228 } 1229 1230 p = obj.Appendp(p, c.newprog) 1231 q = p 1232 p.As = ABLT 1233 p.To.Type = obj.TYPE_BRANCH 1234 } 1235 1236 // Check against the stack guard. We've ensured this won't underflow. 1237 // ADD $-(framesize-StackSmall), SP, R4 1238 // CMPU stackguard, R4 1239 p = obj.Appendp(p, c.newprog) 1240 1241 p.As = AADD 1242 p.From.Type = obj.TYPE_CONST 1243 p.From.Offset = -offset 1244 p.Reg = REGSP 1245 p.To.Type = obj.TYPE_REG 1246 p.To.Reg = REG_R23 1247 1248 p = obj.Appendp(p, c.newprog) 1249 p.As = ACMPU 1250 p.From.Type = obj.TYPE_REG 1251 p.From.Reg = REG_R22 1252 p.To.Type = obj.TYPE_REG 1253 p.To.Reg = REG_R23 1254 } 1255 1256 // q1: BLT done 1257 p = obj.Appendp(p, c.newprog) 1258 q1 := p 1259 1260 p.As = ABLT 1261 p.To.Type = obj.TYPE_BRANCH 1262 1263 p = obj.Appendp(p, c.newprog) 1264 p.As = obj.ANOP // zero-width place holder 1265 1266 if q != nil { 1267 q.To.SetTarget(p) 1268 } 1269 1270 // Spill the register args that could be clobbered by the 1271 // morestack code. 1272 1273 spill := c.cursym.Func().SpillRegisterArgs(p, c.newprog) 1274 1275 // MOVD LR, R5 1276 p = obj.Appendp(spill, c.newprog) 1277 p.As = AMOVD 1278 p.From.Type = obj.TYPE_REG 1279 p.From.Reg = REG_LR 1280 p.To.Type = obj.TYPE_REG 1281 p.To.Reg = REG_R5 1282 1283 p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog) 1284 1285 var morestacksym *obj.LSym 1286 if c.cursym.CFunc() { 1287 morestacksym = c.ctxt.Lookup("runtime.morestackc") 1288 } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { 1289 morestacksym = c.ctxt.Lookup("runtime.morestack_noctxt") 1290 } else { 1291 morestacksym = c.ctxt.Lookup("runtime.morestack") 1292 } 1293 1294 if c.ctxt.Flag_shared { 1295 // In PPC64 PIC code, R2 is used as TOC pointer derived from R12 1296 // which is the address of function entry point when entering 1297 // the function. We need to preserve R2 across call to morestack. 1298 // Fortunately, in shared mode, 8(SP) and 16(SP) are reserved in 1299 // the caller's frame, but not used (0(SP) is caller's saved LR, 1300 // 24(SP) is caller's saved R2). Use 8(SP) to save this function's R2. 1301 // MOVD R2, 8(SP) 1302 p = obj.Appendp(p, c.newprog) 1303 p.As = AMOVD 1304 p.From.Type = obj.TYPE_REG 1305 p.From.Reg = REG_R2 1306 p.To.Type = obj.TYPE_MEM 1307 p.To.Reg = REGSP 1308 p.To.Offset = 8 1309 } 1310 1311 if c.ctxt.Flag_dynlink { 1312 // Avoid calling morestack via a PLT when dynamically linking. The 1313 // PLT stubs generated by the system linker on ppc64le when "std r2, 1314 // 24(r1)" to save the TOC pointer in their callers stack 1315 // frame. Unfortunately (and necessarily) morestack is called before 1316 // the function that calls it sets up its frame and so the PLT ends 1317 // up smashing the saved TOC pointer for its caller's caller. 1318 // 1319 // According to the ABI documentation there is a mechanism to avoid 1320 // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE 1321 // relocation on the nop after the call to morestack) but at the time 1322 // of writing it is not supported at all by gold and my attempt to 1323 // use it with ld.bfd caused an internal linker error. So this hack 1324 // seems preferable. 1325 1326 // MOVD $runtime.morestack(SB), R12 1327 p = obj.Appendp(p, c.newprog) 1328 p.As = AMOVD 1329 p.From.Type = obj.TYPE_MEM 1330 p.From.Sym = morestacksym 1331 p.From.Name = obj.NAME_GOTREF 1332 p.To.Type = obj.TYPE_REG 1333 p.To.Reg = REG_R12 1334 1335 // MOVD R12, LR 1336 p = obj.Appendp(p, c.newprog) 1337 p.As = AMOVD 1338 p.From.Type = obj.TYPE_REG 1339 p.From.Reg = REG_R12 1340 p.To.Type = obj.TYPE_REG 1341 p.To.Reg = REG_LR 1342 1343 // BL LR 1344 p = obj.Appendp(p, c.newprog) 1345 p.As = obj.ACALL 1346 p.To.Type = obj.TYPE_REG 1347 p.To.Reg = REG_LR 1348 } else { 1349 // BL runtime.morestack(SB) 1350 p = obj.Appendp(p, c.newprog) 1351 1352 p.As = ABL 1353 p.To.Type = obj.TYPE_BRANCH 1354 p.To.Sym = morestacksym 1355 } 1356 1357 if c.ctxt.Flag_shared { 1358 // MOVD 8(SP), R2 1359 p = obj.Appendp(p, c.newprog) 1360 p.As = AMOVD 1361 p.From.Type = obj.TYPE_MEM 1362 p.From.Reg = REGSP 1363 p.From.Offset = 8 1364 p.To.Type = obj.TYPE_REG 1365 p.To.Reg = REG_R2 1366 } 1367 1368 unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog) 1369 p = c.ctxt.EndUnsafePoint(unspill, c.newprog, -1) 1370 1371 // BR start 1372 p = obj.Appendp(p, c.newprog) 1373 p.As = ABR 1374 p.To.Type = obj.TYPE_BRANCH 1375 p.To.SetTarget(startPred.Link) 1376 1377 // placeholder for q1's jump target 1378 p = obj.Appendp(p, c.newprog) 1379 1380 p.As = obj.ANOP // zero-width place holder 1381 q1.To.SetTarget(p) 1382 1383 return p 1384 } 1385 1386 // MMA accumulator to/from instructions are slightly ambiguous since 1387 // the argument represents both source and destination, specified as 1388 // an accumulator. It is treated as a unary destination to simplify 1389 // the code generation in ppc64map. 1390 var unaryDst = map[obj.As]bool{ 1391 AXXSETACCZ: true, 1392 AXXMTACC: true, 1393 AXXMFACC: true, 1394 } 1395 1396 var Linkppc64 = obj.LinkArch{ 1397 Arch: sys.ArchPPC64, 1398 Init: buildop, 1399 Preprocess: preprocess, 1400 Assemble: span9, 1401 Progedit: progedit, 1402 UnaryDst: unaryDst, 1403 DWARFRegisters: PPC64DWARFRegisters, 1404 } 1405 1406 var Linkppc64le = obj.LinkArch{ 1407 Arch: sys.ArchPPC64LE, 1408 Init: buildop, 1409 Preprocess: preprocess, 1410 Assemble: span9, 1411 Progedit: progedit, 1412 UnaryDst: unaryDst, 1413 DWARFRegisters: PPC64DWARFRegisters, 1414 }