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