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