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