github.com/gocuntian/go@v0.0.0-20160610041250-fee02d270bf8/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 fmt.Fprintf(ctxt.Bso, "%5.2f noops\n", obj.Cputime()) 279 } 280 ctxt.Bso.Flush() 281 282 var q *obj.Prog 283 var q1 *obj.Prog 284 for p := cursym.Text; p != nil; p = p.Link { 285 switch p.As { 286 /* too hard, just leave alone */ 287 case obj.ATEXT: 288 q = p 289 290 p.Mark |= LABEL | LEAF | SYNC 291 if p.Link != nil { 292 p.Link.Mark |= LABEL 293 } 294 295 case ANOR: 296 q = p 297 if p.To.Type == obj.TYPE_REG { 298 if p.To.Reg == REGZERO { 299 p.Mark |= LABEL | SYNC 300 } 301 } 302 303 case ALWAR, 304 ALBAR, 305 ASTBCCC, 306 ASTWCCC, 307 AECIWX, 308 AECOWX, 309 AEIEIO, 310 AICBI, 311 AISYNC, 312 ATLBIE, 313 ATLBIEL, 314 ASLBIA, 315 ASLBIE, 316 ASLBMFEE, 317 ASLBMFEV, 318 ASLBMTE, 319 ADCBF, 320 ADCBI, 321 ADCBST, 322 ADCBT, 323 ADCBTST, 324 ADCBZ, 325 ASYNC, 326 ATLBSYNC, 327 APTESYNC, 328 ALWSYNC, 329 ATW, 330 AWORD, 331 ARFI, 332 ARFCI, 333 ARFID, 334 AHRFID: 335 q = p 336 p.Mark |= LABEL | SYNC 337 continue 338 339 case AMOVW, AMOVWZ, AMOVD: 340 q = p 341 if p.From.Reg >= REG_SPECIAL || p.To.Reg >= REG_SPECIAL { 342 p.Mark |= LABEL | SYNC 343 } 344 continue 345 346 case AFABS, 347 AFABSCC, 348 AFADD, 349 AFADDCC, 350 AFCTIW, 351 AFCTIWCC, 352 AFCTIWZ, 353 AFCTIWZCC, 354 AFDIV, 355 AFDIVCC, 356 AFMADD, 357 AFMADDCC, 358 AFMOVD, 359 AFMOVDU, 360 /* case AFMOVDS: */ 361 AFMOVS, 362 AFMOVSU, 363 364 /* case AFMOVSD: */ 365 AFMSUB, 366 AFMSUBCC, 367 AFMUL, 368 AFMULCC, 369 AFNABS, 370 AFNABSCC, 371 AFNEG, 372 AFNEGCC, 373 AFNMADD, 374 AFNMADDCC, 375 AFNMSUB, 376 AFNMSUBCC, 377 AFRSP, 378 AFRSPCC, 379 AFSUB, 380 AFSUBCC: 381 q = p 382 383 p.Mark |= FLOAT 384 continue 385 386 case ABL, 387 ABCL, 388 obj.ADUFFZERO, 389 obj.ADUFFCOPY: 390 cursym.Text.Mark &^= LEAF 391 fallthrough 392 393 case ABC, 394 ABEQ, 395 ABGE, 396 ABGT, 397 ABLE, 398 ABLT, 399 ABNE, 400 ABR, 401 ABVC, 402 ABVS: 403 p.Mark |= BRANCH 404 q = p 405 q1 = p.Pcond 406 if q1 != nil { 407 for q1.As == obj.ANOP { 408 q1 = q1.Link 409 p.Pcond = q1 410 } 411 412 if q1.Mark&LEAF == 0 { 413 q1.Mark |= LABEL 414 } 415 } else { 416 p.Mark |= LABEL 417 } 418 q1 = p.Link 419 if q1 != nil { 420 q1.Mark |= LABEL 421 } 422 continue 423 424 case AFCMPO, AFCMPU: 425 q = p 426 p.Mark |= FCMP | FLOAT 427 continue 428 429 case obj.ARET: 430 q = p 431 if p.Link != nil { 432 p.Link.Mark |= LABEL 433 } 434 continue 435 436 case obj.ANOP: 437 q1 = p.Link 438 q.Link = q1 /* q is non-nop */ 439 q1.Mark |= p.Mark 440 continue 441 442 default: 443 q = p 444 continue 445 } 446 } 447 448 autosize := int32(0) 449 var aoffset int 450 var mov obj.As 451 var p1 *obj.Prog 452 var p2 *obj.Prog 453 for p := cursym.Text; p != nil; p = p.Link { 454 o := p.As 455 switch o { 456 case obj.ATEXT: 457 mov = AMOVD 458 aoffset = 0 459 autosize = int32(textstksiz) 460 461 if p.Mark&LEAF != 0 && autosize == 0 && p.From3.Offset&obj.NOFRAME == 0 { 462 // A leaf function with no locals has no frame. 463 p.From3.Offset |= obj.NOFRAME 464 } 465 466 if p.From3.Offset&obj.NOFRAME == 0 { 467 // If there is a stack frame at all, it includes 468 // space to save the LR. 469 autosize += int32(ctxt.FixedFrameSize()) 470 } 471 472 p.To.Offset = int64(autosize) 473 474 q = p 475 476 if ctxt.Flag_shared && cursym.Name != "runtime.duffzero" && cursym.Name != "runtime.duffcopy" && cursym.Name != "runtime.stackBarrier" { 477 // When compiling Go into PIC, all functions must start 478 // with instructions to load the TOC pointer into r2: 479 // 480 // addis r2, r12, .TOC.-func@ha 481 // addi r2, r2, .TOC.-func@l+4 482 // 483 // We could probably skip this prologue in some situations 484 // but it's a bit subtle. However, it is both safe and 485 // necessary to leave the prologue off duffzero and 486 // duffcopy as we rely on being able to jump to a specific 487 // instruction offset for them, and stackBarrier is only 488 // ever called from an overwritten LR-save slot on the 489 // stack (when r12 will not be remotely the right thing) 490 // but fortunately does not access global data. 491 // 492 // These are AWORDS because there is no (afaict) way to 493 // generate the addis instruction except as part of the 494 // load of a large constant, and in that case there is no 495 // way to use r12 as the source. 496 q = obj.Appendp(ctxt, q) 497 q.As = AWORD 498 q.Lineno = p.Lineno 499 q.From.Type = obj.TYPE_CONST 500 q.From.Offset = 0x3c4c0000 501 q = obj.Appendp(ctxt, q) 502 q.As = AWORD 503 q.Lineno = p.Lineno 504 q.From.Type = obj.TYPE_CONST 505 q.From.Offset = 0x38420000 506 rel := obj.Addrel(ctxt.Cursym) 507 rel.Off = 0 508 rel.Siz = 8 509 rel.Sym = obj.Linklookup(ctxt, ".TOC.", 0) 510 rel.Type = obj.R_ADDRPOWER_PCREL 511 } 512 513 if cursym.Text.From3.Offset&obj.NOSPLIT == 0 { 514 q = stacksplit(ctxt, q, autosize) // emit split check 515 } 516 517 if autosize != 0 { 518 /* use MOVDU to adjust R1 when saving R31, if autosize is small */ 519 if cursym.Text.Mark&LEAF == 0 && autosize >= -BIG && autosize <= BIG { 520 mov = AMOVDU 521 aoffset = int(-autosize) 522 } else { 523 q = obj.Appendp(ctxt, q) 524 q.As = AADD 525 q.Lineno = p.Lineno 526 q.From.Type = obj.TYPE_CONST 527 q.From.Offset = int64(-autosize) 528 q.To.Type = obj.TYPE_REG 529 q.To.Reg = REGSP 530 q.Spadj = +autosize 531 } 532 } else if cursym.Text.Mark&LEAF == 0 { 533 // A very few functions that do not return to their caller 534 // (e.g. gogo) are not identified as leaves but still have 535 // no frame. 536 cursym.Text.Mark |= LEAF 537 } 538 539 if cursym.Text.Mark&LEAF != 0 { 540 cursym.Leaf = true 541 break 542 } 543 544 q = obj.Appendp(ctxt, q) 545 q.As = AMOVD 546 q.Lineno = p.Lineno 547 q.From.Type = obj.TYPE_REG 548 q.From.Reg = REG_LR 549 q.To.Type = obj.TYPE_REG 550 q.To.Reg = REGTMP 551 552 q = obj.Appendp(ctxt, q) 553 q.As = mov 554 q.Lineno = p.Lineno 555 q.From.Type = obj.TYPE_REG 556 q.From.Reg = REGTMP 557 q.To.Type = obj.TYPE_MEM 558 q.To.Offset = int64(aoffset) 559 q.To.Reg = REGSP 560 if q.As == AMOVDU { 561 q.Spadj = int32(-aoffset) 562 } 563 564 if ctxt.Flag_shared { 565 q = obj.Appendp(ctxt, q) 566 q.As = AMOVD 567 q.Lineno = p.Lineno 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 cursym.Text.From3.Offset&obj.WRAPPER != 0 { 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(ctxt, q) 594 595 q.As = AMOVD 596 q.From.Type = obj.TYPE_MEM 597 q.From.Reg = REGG 598 q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic 599 q.To.Type = obj.TYPE_REG 600 q.To.Reg = REG_R3 601 602 q = obj.Appendp(ctxt, q) 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(ctxt, q) 610 q.As = ABEQ 611 q.To.Type = obj.TYPE_BRANCH 612 p1 = q 613 614 q = obj.Appendp(ctxt, q) 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(ctxt, q) 623 q.As = AADD 624 q.From.Type = obj.TYPE_CONST 625 q.From.Offset = int64(autosize) + ctxt.FixedFrameSize() 626 q.Reg = REGSP 627 q.To.Type = obj.TYPE_REG 628 q.To.Reg = REG_R5 629 630 q = obj.Appendp(ctxt, q) 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(ctxt, q) 638 q.As = ABNE 639 q.To.Type = obj.TYPE_BRANCH 640 p2 = q 641 642 q = obj.Appendp(ctxt, q) 643 q.As = AADD 644 q.From.Type = obj.TYPE_CONST 645 q.From.Offset = ctxt.FixedFrameSize() 646 q.Reg = REGSP 647 q.To.Type = obj.TYPE_REG 648 q.To.Reg = REG_R6 649 650 q = obj.Appendp(ctxt, q) 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(ctxt, q) 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 ctxt.Diag("using BECOME (%v) is not supported!", p) 668 break 669 } 670 671 retTarget := p.To.Sym 672 673 if 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 = ctxt.NewProg() 696 q.As = ABR 697 q.Lineno = p.Lineno 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 = ctxt.NewProg() 716 q.As = AMOVD 717 q.Lineno = p.Lineno 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 = ctxt.NewProg() 730 731 q.As = AMOVD 732 q.Lineno = p.Lineno 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 = ctxt.NewProg() 746 q.As = AADD 747 q.Lineno = p.Lineno 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 = ctxt.NewProg() 759 q1.As = ABR 760 q1.Lineno = p.Lineno 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 stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog { 827 // MOVD g_stackguard(g), R3 828 p = obj.Appendp(ctxt, p) 829 830 p.As = AMOVD 831 p.From.Type = obj.TYPE_MEM 832 p.From.Reg = REGG 833 p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0 834 if ctxt.Cursym.Cfunc { 835 p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1 836 } 837 p.To.Type = obj.TYPE_REG 838 p.To.Reg = REG_R3 839 840 var q *obj.Prog 841 if framesize <= obj.StackSmall { 842 // small stack: SP < stackguard 843 // CMP stackguard, SP 844 p = obj.Appendp(ctxt, p) 845 846 p.As = ACMPU 847 p.From.Type = obj.TYPE_REG 848 p.From.Reg = REG_R3 849 p.To.Type = obj.TYPE_REG 850 p.To.Reg = REGSP 851 } else if framesize <= obj.StackBig { 852 // large stack: SP-framesize < stackguard-StackSmall 853 // ADD $-framesize, SP, R4 854 // CMP stackguard, R4 855 p = obj.Appendp(ctxt, p) 856 857 p.As = AADD 858 p.From.Type = obj.TYPE_CONST 859 p.From.Offset = int64(-framesize) 860 p.Reg = REGSP 861 p.To.Type = obj.TYPE_REG 862 p.To.Reg = REG_R4 863 864 p = obj.Appendp(ctxt, p) 865 p.As = ACMPU 866 p.From.Type = obj.TYPE_REG 867 p.From.Reg = REG_R3 868 p.To.Type = obj.TYPE_REG 869 p.To.Reg = REG_R4 870 } else { 871 // Such a large stack we need to protect against wraparound. 872 // If SP is close to zero: 873 // SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall) 874 // The +StackGuard on both sides is required to keep the left side positive: 875 // SP is allowed to be slightly below stackguard. See stack.h. 876 // 877 // Preemption sets stackguard to StackPreempt, a very large value. 878 // That breaks the math above, so we have to check for that explicitly. 879 // // stackguard is R3 880 // CMP R3, $StackPreempt 881 // BEQ label-of-call-to-morestack 882 // ADD $StackGuard, SP, R4 883 // SUB R3, R4 884 // MOVD $(framesize+(StackGuard-StackSmall)), R31 885 // CMPU R31, R4 886 p = obj.Appendp(ctxt, p) 887 888 p.As = ACMP 889 p.From.Type = obj.TYPE_REG 890 p.From.Reg = REG_R3 891 p.To.Type = obj.TYPE_CONST 892 p.To.Offset = obj.StackPreempt 893 894 p = obj.Appendp(ctxt, p) 895 q = p 896 p.As = ABEQ 897 p.To.Type = obj.TYPE_BRANCH 898 899 p = obj.Appendp(ctxt, p) 900 p.As = AADD 901 p.From.Type = obj.TYPE_CONST 902 p.From.Offset = obj.StackGuard 903 p.Reg = REGSP 904 p.To.Type = obj.TYPE_REG 905 p.To.Reg = REG_R4 906 907 p = obj.Appendp(ctxt, p) 908 p.As = ASUB 909 p.From.Type = obj.TYPE_REG 910 p.From.Reg = REG_R3 911 p.To.Type = obj.TYPE_REG 912 p.To.Reg = REG_R4 913 914 p = obj.Appendp(ctxt, p) 915 p.As = AMOVD 916 p.From.Type = obj.TYPE_CONST 917 p.From.Offset = int64(framesize) + obj.StackGuard - obj.StackSmall 918 p.To.Type = obj.TYPE_REG 919 p.To.Reg = REGTMP 920 921 p = obj.Appendp(ctxt, p) 922 p.As = ACMPU 923 p.From.Type = obj.TYPE_REG 924 p.From.Reg = REGTMP 925 p.To.Type = obj.TYPE_REG 926 p.To.Reg = REG_R4 927 } 928 929 // q1: BLT done 930 p = obj.Appendp(ctxt, p) 931 q1 := p 932 933 p.As = ABLT 934 p.To.Type = obj.TYPE_BRANCH 935 936 // MOVD LR, R5 937 p = obj.Appendp(ctxt, p) 938 939 p.As = AMOVD 940 p.From.Type = obj.TYPE_REG 941 p.From.Reg = REG_LR 942 p.To.Type = obj.TYPE_REG 943 p.To.Reg = REG_R5 944 if q != nil { 945 q.Pcond = p 946 } 947 948 var morestacksym *obj.LSym 949 if ctxt.Cursym.Cfunc { 950 morestacksym = obj.Linklookup(ctxt, "runtime.morestackc", 0) 951 } else if ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0 { 952 morestacksym = obj.Linklookup(ctxt, "runtime.morestack_noctxt", 0) 953 } else { 954 morestacksym = obj.Linklookup(ctxt, "runtime.morestack", 0) 955 } 956 957 if ctxt.Flag_dynlink { 958 // Avoid calling morestack via a PLT when dynamically linking. The 959 // PLT stubs generated by the system linker on ppc64le when "std r2, 960 // 24(r1)" to save the TOC pointer in their callers stack 961 // frame. Unfortunately (and necessarily) morestack is called before 962 // the function that calls it sets up its frame and so the PLT ends 963 // up smashing the saved TOC pointer for its caller's caller. 964 // 965 // According to the ABI documentation there is a mechanism to avoid 966 // the TOC save that the PLT stub does (put a R_PPC64_TOCSAVE 967 // relocation on the nop after the call to morestack) but at the time 968 // of writing it is not supported at all by gold and my attempt to 969 // use it with ld.bfd caused an internal linker error. So this hack 970 // seems preferable. 971 972 // MOVD $runtime.morestack(SB), R12 973 p = obj.Appendp(ctxt, p) 974 p.As = AMOVD 975 p.From.Type = obj.TYPE_MEM 976 p.From.Sym = morestacksym 977 p.From.Name = obj.NAME_GOTREF 978 p.To.Type = obj.TYPE_REG 979 p.To.Reg = REG_R12 980 981 // MOVD R12, CTR 982 p = obj.Appendp(ctxt, p) 983 p.As = AMOVD 984 p.From.Type = obj.TYPE_REG 985 p.From.Reg = REG_R12 986 p.To.Type = obj.TYPE_REG 987 p.To.Reg = REG_CTR 988 989 // BL CTR 990 p = obj.Appendp(ctxt, p) 991 p.As = obj.ACALL 992 p.From.Type = obj.TYPE_REG 993 p.From.Reg = REG_R12 994 p.To.Type = obj.TYPE_REG 995 p.To.Reg = REG_CTR 996 } else { 997 // BL runtime.morestack(SB) 998 p = obj.Appendp(ctxt, p) 999 1000 p.As = ABL 1001 p.To.Type = obj.TYPE_BRANCH 1002 p.To.Sym = morestacksym 1003 } 1004 // BR start 1005 p = obj.Appendp(ctxt, p) 1006 1007 p.As = ABR 1008 p.To.Type = obj.TYPE_BRANCH 1009 p.Pcond = ctxt.Cursym.Text.Link 1010 1011 // placeholder for q1's jump target 1012 p = obj.Appendp(ctxt, p) 1013 1014 p.As = obj.ANOP // zero-width place holder 1015 q1.Pcond = p 1016 1017 return p 1018 } 1019 1020 func follow(ctxt *obj.Link, s *obj.LSym) { 1021 ctxt.Cursym = s 1022 1023 firstp := ctxt.NewProg() 1024 lastp := firstp 1025 xfol(ctxt, s.Text, &lastp) 1026 lastp.Link = nil 1027 s.Text = firstp.Link 1028 } 1029 1030 func relinv(a obj.As) obj.As { 1031 switch a { 1032 case ABEQ: 1033 return ABNE 1034 case ABNE: 1035 return ABEQ 1036 1037 case ABGE: 1038 return ABLT 1039 case ABLT: 1040 return ABGE 1041 1042 case ABGT: 1043 return ABLE 1044 case ABLE: 1045 return ABGT 1046 1047 case ABVC: 1048 return ABVS 1049 case ABVS: 1050 return ABVC 1051 } 1052 1053 return 0 1054 } 1055 1056 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) { 1057 var q *obj.Prog 1058 var r *obj.Prog 1059 var b obj.As 1060 var i int 1061 1062 loop: 1063 if p == nil { 1064 return 1065 } 1066 a := p.As 1067 if a == ABR { 1068 q = p.Pcond 1069 if (p.Mark&NOSCHED != 0) || q != nil && (q.Mark&NOSCHED != 0) { 1070 p.Mark |= FOLL 1071 (*last).Link = p 1072 *last = p 1073 p = p.Link 1074 xfol(ctxt, p, last) 1075 p = q 1076 if p != nil && p.Mark&FOLL == 0 { 1077 goto loop 1078 } 1079 return 1080 } 1081 1082 if q != nil { 1083 p.Mark |= FOLL 1084 p = q 1085 if p.Mark&FOLL == 0 { 1086 goto loop 1087 } 1088 } 1089 } 1090 1091 if p.Mark&FOLL != 0 { 1092 i = 0 1093 q = p 1094 for ; i < 4; i, q = i+1, q.Link { 1095 if q == *last || (q.Mark&NOSCHED != 0) { 1096 break 1097 } 1098 b = 0 /* set */ 1099 a = q.As 1100 if a == obj.ANOP { 1101 i-- 1102 continue 1103 } 1104 1105 if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { 1106 goto copy 1107 } 1108 if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) { 1109 continue 1110 } 1111 b = relinv(a) 1112 if b == 0 { 1113 continue 1114 } 1115 1116 copy: 1117 for { 1118 r = ctxt.NewProg() 1119 *r = *p 1120 if r.Mark&FOLL == 0 { 1121 fmt.Printf("can't happen 1\n") 1122 } 1123 r.Mark |= FOLL 1124 if p != q { 1125 p = p.Link 1126 (*last).Link = r 1127 *last = r 1128 continue 1129 } 1130 1131 (*last).Link = r 1132 *last = r 1133 if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { 1134 return 1135 } 1136 r.As = b 1137 r.Pcond = p.Link 1138 r.Link = p.Pcond 1139 if r.Link.Mark&FOLL == 0 { 1140 xfol(ctxt, r.Link, last) 1141 } 1142 if r.Pcond.Mark&FOLL == 0 { 1143 fmt.Printf("can't happen 2\n") 1144 } 1145 return 1146 } 1147 } 1148 1149 a = ABR 1150 q = ctxt.NewProg() 1151 q.As = a 1152 q.Lineno = p.Lineno 1153 q.To.Type = obj.TYPE_BRANCH 1154 q.To.Offset = p.Pc 1155 q.Pcond = p 1156 p = q 1157 } 1158 1159 p.Mark |= FOLL 1160 (*last).Link = p 1161 *last = p 1162 if a == ABR || a == obj.ARET || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID { 1163 if p.Mark&NOSCHED != 0 { 1164 p = p.Link 1165 goto loop 1166 } 1167 1168 return 1169 } 1170 1171 if p.Pcond != nil { 1172 if a != ABL && p.Link != nil { 1173 xfol(ctxt, p.Link, last) 1174 p = p.Pcond 1175 if p == nil || (p.Mark&FOLL != 0) { 1176 return 1177 } 1178 goto loop 1179 } 1180 } 1181 1182 p = p.Link 1183 goto loop 1184 } 1185 1186 var Linkppc64 = obj.LinkArch{ 1187 Arch: sys.ArchPPC64, 1188 Preprocess: preprocess, 1189 Assemble: span9, 1190 Follow: follow, 1191 Progedit: progedit, 1192 } 1193 1194 var Linkppc64le = obj.LinkArch{ 1195 Arch: sys.ArchPPC64LE, 1196 Preprocess: preprocess, 1197 Assemble: span9, 1198 Follow: follow, 1199 Progedit: progedit, 1200 }