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