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