github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/s390x/objz.go (about) 1 // Based on github.com/go-asm/go/cmd/obj/ppc64/obj9.go. 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 s390x 31 32 import ( 33 "log" 34 "math" 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/sys" 40 ) 41 42 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) { 43 p.From.Class = 0 44 p.To.Class = 0 45 46 c := ctxtz{ctxt: ctxt, newprog: newprog} 47 48 // Rewrite BR/BL to symbol as TYPE_BRANCH. 49 switch p.As { 50 case ABR, ABL, obj.ARET, obj.ADUFFZERO, 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 unless they are +0. 57 switch p.As { 58 case AFMOVS: 59 if p.From.Type == obj.TYPE_FCONST { 60 f32 := float32(p.From.Val.(float64)) 61 if math.Float32bits(f32) == 0 { // +0 62 break 63 } 64 p.From.Type = obj.TYPE_MEM 65 p.From.Sym = ctxt.Float32Sym(f32) 66 p.From.Name = obj.NAME_EXTERN 67 p.From.Offset = 0 68 } 69 70 case AFMOVD: 71 if p.From.Type == obj.TYPE_FCONST { 72 f64 := p.From.Val.(float64) 73 if math.Float64bits(f64) == 0 { // +0 74 break 75 } 76 p.From.Type = obj.TYPE_MEM 77 p.From.Sym = ctxt.Float64Sym(f64) 78 p.From.Name = obj.NAME_EXTERN 79 p.From.Offset = 0 80 } 81 82 // put constants not loadable by LOAD IMMEDIATE into memory 83 case AMOVD: 84 if p.From.Type == obj.TYPE_CONST { 85 val := p.From.Offset 86 if int64(int32(val)) != val && 87 int64(uint32(val)) != val && 88 int64(uint64(val)&(0xffffffff<<32)) != val { 89 p.From.Type = obj.TYPE_MEM 90 p.From.Sym = ctxt.Int64Sym(p.From.Offset) 91 p.From.Name = obj.NAME_EXTERN 92 p.From.Offset = 0 93 } 94 } 95 } 96 97 // Rewrite SUB constants into ADD. 98 switch p.As { 99 case ASUBC: 100 if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) { 101 p.From.Offset = -p.From.Offset 102 p.As = AADDC 103 } 104 105 case ASUB: 106 if p.From.Type == obj.TYPE_CONST && isint32(-p.From.Offset) { 107 p.From.Offset = -p.From.Offset 108 p.As = AADD 109 } 110 } 111 112 if c.ctxt.Flag_dynlink { 113 c.rewriteToUseGot(p) 114 } 115 } 116 117 // Rewrite p, if necessary, to access global data via the global offset table. 118 func (c *ctxtz) rewriteToUseGot(p *obj.Prog) { 119 // At the moment EXRL instructions are not emitted by the compiler and only reference local symbols in 120 // assembly code. 121 if p.As == AEXRL { 122 return 123 } 124 125 // We only care about global data: NAME_EXTERN means a global 126 // symbol in the Go sense, and p.Sym.Local is true for a few 127 // internally defined symbols. 128 // Rewrites must not clobber flags and therefore cannot use the 129 // ADD instruction. 130 if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 131 // MOVD $sym, Rx becomes MOVD sym@GOT, Rx 132 // MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx or REGTMP2; MOVD $<off>(Rx or REGTMP2), Rx 133 if p.To.Type != obj.TYPE_REG || p.As != AMOVD { 134 c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p) 135 } 136 p.From.Type = obj.TYPE_MEM 137 p.From.Name = obj.NAME_GOTREF 138 q := p 139 if p.From.Offset != 0 { 140 target := p.To.Reg 141 if target == REG_R0 { 142 // Cannot use R0 as input to address calculation. 143 // REGTMP might be used by the assembler. 144 p.To.Reg = REGTMP2 145 } 146 q = obj.Appendp(q, c.newprog) 147 q.As = AMOVD 148 q.From.Type = obj.TYPE_ADDR 149 q.From.Offset = p.From.Offset 150 q.From.Reg = p.To.Reg 151 q.To.Type = obj.TYPE_REG 152 q.To.Reg = target 153 p.From.Offset = 0 154 } 155 } 156 if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN { 157 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 158 } 159 var source *obj.Addr 160 // MOVD sym, Ry becomes MOVD sym@GOT, REGTMP2; MOVD (REGTMP2), Ry 161 // MOVD Ry, sym becomes MOVD sym@GOT, REGTMP2; MOVD Ry, (REGTMP2) 162 // An addition may be inserted between the two MOVs if there is an offset. 163 if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() { 164 if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 165 c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p) 166 } 167 source = &p.From 168 } else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() { 169 source = &p.To 170 } else { 171 return 172 } 173 if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP { 174 return 175 } 176 if source.Sym.Type == objabi.STLSBSS { 177 return 178 } 179 if source.Type != obj.TYPE_MEM { 180 c.ctxt.Diag("don't know how to handle %v with -dynlink", p) 181 } 182 p1 := obj.Appendp(p, c.newprog) 183 p2 := obj.Appendp(p1, c.newprog) 184 185 p1.As = AMOVD 186 p1.From.Type = obj.TYPE_MEM 187 p1.From.Sym = source.Sym 188 p1.From.Name = obj.NAME_GOTREF 189 p1.To.Type = obj.TYPE_REG 190 p1.To.Reg = REGTMP2 191 192 p2.As = p.As 193 p2.From = p.From 194 p2.To = p.To 195 if p.From.Name == obj.NAME_EXTERN { 196 p2.From.Reg = REGTMP2 197 p2.From.Name = obj.NAME_NONE 198 p2.From.Sym = nil 199 } else if p.To.Name == obj.NAME_EXTERN { 200 p2.To.Reg = REGTMP2 201 p2.To.Name = obj.NAME_NONE 202 p2.To.Sym = nil 203 } else { 204 return 205 } 206 obj.Nopout(p) 207 } 208 209 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) { 210 // TODO(minux): add morestack short-cuts with small fixed frame-size. 211 if cursym.Func().Text == nil || cursym.Func().Text.Link == nil { 212 return 213 } 214 215 c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog} 216 217 p := c.cursym.Func().Text 218 textstksiz := p.To.Offset 219 if textstksiz == -8 { 220 // Compatibility hack. 221 p.From.Sym.Set(obj.AttrNoFrame, true) 222 textstksiz = 0 223 } 224 if textstksiz%8 != 0 { 225 c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz) 226 } 227 if p.From.Sym.NoFrame() { 228 if textstksiz != 0 { 229 c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz) 230 } 231 } 232 233 c.cursym.Func().Args = p.To.Val.(int32) 234 c.cursym.Func().Locals = int32(textstksiz) 235 236 /* 237 * find leaf subroutines 238 * strip NOPs 239 * expand RET 240 */ 241 242 var q *obj.Prog 243 for p := c.cursym.Func().Text; p != nil; p = p.Link { 244 switch p.As { 245 case obj.ATEXT: 246 q = p 247 p.Mark |= LEAF 248 249 case ABL, ABCL: 250 q = p 251 c.cursym.Func().Text.Mark &^= LEAF 252 fallthrough 253 254 case ABC, 255 ABRC, 256 ABEQ, 257 ABGE, 258 ABGT, 259 ABLE, 260 ABLT, 261 ABLEU, 262 ABLTU, 263 ABNE, 264 ABR, 265 ABVC, 266 ABVS, 267 ACRJ, 268 ACGRJ, 269 ACLRJ, 270 ACLGRJ, 271 ACIJ, 272 ACGIJ, 273 ACLIJ, 274 ACLGIJ, 275 ACMPBEQ, 276 ACMPBGE, 277 ACMPBGT, 278 ACMPBLE, 279 ACMPBLT, 280 ACMPBNE, 281 ACMPUBEQ, 282 ACMPUBGE, 283 ACMPUBGT, 284 ACMPUBLE, 285 ACMPUBLT, 286 ACMPUBNE: 287 q = p 288 p.Mark |= BRANCH 289 290 default: 291 q = p 292 } 293 } 294 295 autosize := int32(0) 296 var pLast *obj.Prog 297 var pPre *obj.Prog 298 var pPreempt *obj.Prog 299 var pCheck *obj.Prog 300 wasSplit := false 301 for p := c.cursym.Func().Text; p != nil; p = p.Link { 302 pLast = p 303 switch p.As { 304 case obj.ATEXT: 305 autosize = int32(textstksiz) 306 307 if p.Mark&LEAF != 0 && autosize == 0 { 308 // A leaf function with no locals has no frame. 309 p.From.Sym.Set(obj.AttrNoFrame, true) 310 } 311 312 if !p.From.Sym.NoFrame() { 313 // If there is a stack frame at all, it includes 314 // space to save the LR. 315 autosize += int32(c.ctxt.Arch.FixedFrameSize) 316 } 317 318 if p.Mark&LEAF != 0 && autosize < abi.StackSmall { 319 // A leaf function with a small stack can be marked 320 // NOSPLIT, avoiding a stack check. 321 p.From.Sym.Set(obj.AttrNoSplit, true) 322 } 323 324 p.To.Offset = int64(autosize) 325 326 q := p 327 328 if !p.From.Sym.NoSplit() { 329 p, pPreempt, pCheck = c.stacksplitPre(p, autosize) // emit pre part of split check 330 pPre = p 331 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 332 wasSplit = true //need post part of split 333 } 334 335 if autosize != 0 { 336 // Make sure to save link register for non-empty frame, even if 337 // it is a leaf function, so that traceback works. 338 // Store link register before decrementing SP, so if a signal comes 339 // during the execution of the function prologue, the traceback 340 // code will not see a half-updated stack frame. 341 // This sequence is not async preemptible, as if we open a frame 342 // at the current SP, it will clobber the saved LR. 343 q = c.ctxt.StartUnsafePoint(p, c.newprog) 344 345 q = obj.Appendp(q, c.newprog) 346 q.As = AMOVD 347 q.From.Type = obj.TYPE_REG 348 q.From.Reg = REG_LR 349 q.To.Type = obj.TYPE_MEM 350 q.To.Reg = REGSP 351 q.To.Offset = int64(-autosize) 352 353 q = obj.Appendp(q, c.newprog) 354 q.As = AMOVD 355 q.From.Type = obj.TYPE_ADDR 356 q.From.Offset = int64(-autosize) 357 q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided 358 q.To.Type = obj.TYPE_REG 359 q.To.Reg = REGSP 360 q.Spadj = autosize 361 362 q = c.ctxt.EndUnsafePoint(q, c.newprog, -1) 363 364 // On Linux, in a cgo binary we may get a SIGSETXID signal early on 365 // before the signal stack is set, as glibc doesn't allow us to block 366 // SIGSETXID. So a signal may land on the current stack and clobber 367 // the content below the SP. We store the LR again after the SP is 368 // decremented. 369 q = obj.Appendp(q, c.newprog) 370 q.As = AMOVD 371 q.From.Type = obj.TYPE_REG 372 q.From.Reg = REG_LR 373 q.To.Type = obj.TYPE_MEM 374 q.To.Reg = REGSP 375 q.To.Offset = 0 376 } else if c.cursym.Func().Text.Mark&LEAF == 0 { 377 // A very few functions that do not return to their caller 378 // (e.g. gogo) are not identified as leaves but still have 379 // no frame. 380 c.cursym.Func().Text.Mark |= LEAF 381 } 382 383 if c.cursym.Func().Text.Mark&LEAF != 0 { 384 c.cursym.Set(obj.AttrLeaf, true) 385 break 386 } 387 388 if c.cursym.Func().Text.From.Sym.Wrapper() { 389 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 390 // 391 // MOVD g_panic(g), R3 392 // CMP R3, $0 393 // BEQ end 394 // MOVD panic_argp(R3), R4 395 // ADD $(autosize+8), R1, R5 396 // CMP R4, R5 397 // BNE end 398 // ADD $8, R1, R6 399 // MOVD R6, panic_argp(R3) 400 // end: 401 // NOP 402 // 403 // The NOP is needed to give the jumps somewhere to land. 404 // It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes. 405 406 q = obj.Appendp(q, c.newprog) 407 408 q.As = AMOVD 409 q.From.Type = obj.TYPE_MEM 410 q.From.Reg = REGG 411 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 412 q.To.Type = obj.TYPE_REG 413 q.To.Reg = REG_R3 414 415 q = obj.Appendp(q, c.newprog) 416 q.As = ACMP 417 q.From.Type = obj.TYPE_REG 418 q.From.Reg = REG_R3 419 q.To.Type = obj.TYPE_CONST 420 q.To.Offset = 0 421 422 q = obj.Appendp(q, c.newprog) 423 q.As = ABEQ 424 q.To.Type = obj.TYPE_BRANCH 425 p1 := q 426 427 q = obj.Appendp(q, c.newprog) 428 q.As = AMOVD 429 q.From.Type = obj.TYPE_MEM 430 q.From.Reg = REG_R3 431 q.From.Offset = 0 // Panic.argp 432 q.To.Type = obj.TYPE_REG 433 q.To.Reg = REG_R4 434 435 q = obj.Appendp(q, c.newprog) 436 q.As = AADD 437 q.From.Type = obj.TYPE_CONST 438 q.From.Offset = int64(autosize) + c.ctxt.Arch.FixedFrameSize 439 q.Reg = REGSP 440 q.To.Type = obj.TYPE_REG 441 q.To.Reg = REG_R5 442 443 q = obj.Appendp(q, c.newprog) 444 q.As = ACMP 445 q.From.Type = obj.TYPE_REG 446 q.From.Reg = REG_R4 447 q.To.Type = obj.TYPE_REG 448 q.To.Reg = REG_R5 449 450 q = obj.Appendp(q, c.newprog) 451 q.As = ABNE 452 q.To.Type = obj.TYPE_BRANCH 453 p2 := q 454 455 q = obj.Appendp(q, c.newprog) 456 q.As = AADD 457 q.From.Type = obj.TYPE_CONST 458 q.From.Offset = c.ctxt.Arch.FixedFrameSize 459 q.Reg = REGSP 460 q.To.Type = obj.TYPE_REG 461 q.To.Reg = REG_R6 462 463 q = obj.Appendp(q, c.newprog) 464 q.As = AMOVD 465 q.From.Type = obj.TYPE_REG 466 q.From.Reg = REG_R6 467 q.To.Type = obj.TYPE_MEM 468 q.To.Reg = REG_R3 469 q.To.Offset = 0 // Panic.argp 470 471 q = obj.Appendp(q, c.newprog) 472 473 q.As = obj.ANOP 474 p1.To.SetTarget(q) 475 p2.To.SetTarget(q) 476 } 477 478 case obj.ARET: 479 retTarget := p.To.Sym 480 481 if c.cursym.Func().Text.Mark&LEAF != 0 { 482 if autosize == 0 { 483 p.As = ABR 484 p.From = obj.Addr{} 485 if retTarget == nil { 486 p.To.Type = obj.TYPE_REG 487 p.To.Reg = REG_LR 488 } else { 489 p.To.Type = obj.TYPE_BRANCH 490 p.To.Sym = retTarget 491 } 492 p.Mark |= BRANCH 493 break 494 } 495 496 p.As = AADD 497 p.From.Type = obj.TYPE_CONST 498 p.From.Offset = int64(autosize) 499 p.To.Type = obj.TYPE_REG 500 p.To.Reg = REGSP 501 p.Spadj = -autosize 502 503 q = obj.Appendp(p, c.newprog) 504 q.As = ABR 505 q.From = obj.Addr{} 506 if retTarget == nil { 507 q.To.Type = obj.TYPE_REG 508 q.To.Reg = REG_LR 509 } else { 510 q.To.Type = obj.TYPE_BRANCH 511 q.To.Sym = retTarget 512 } 513 q.Mark |= BRANCH 514 q.Spadj = autosize 515 break 516 } 517 518 p.As = AMOVD 519 p.From.Type = obj.TYPE_MEM 520 p.From.Reg = REGSP 521 p.From.Offset = 0 522 p.To = obj.Addr{ 523 Type: obj.TYPE_REG, 524 Reg: REG_LR, 525 } 526 527 q = p 528 529 if autosize != 0 { 530 q = obj.Appendp(q, c.newprog) 531 q.As = AADD 532 q.From.Type = obj.TYPE_CONST 533 q.From.Offset = int64(autosize) 534 q.To.Type = obj.TYPE_REG 535 q.To.Reg = REGSP 536 q.Spadj = -autosize 537 } 538 539 q = obj.Appendp(q, c.newprog) 540 q.As = ABR 541 q.From = obj.Addr{} 542 if retTarget == nil { 543 q.To.Type = obj.TYPE_REG 544 q.To.Reg = REG_LR 545 } else { 546 q.To.Type = obj.TYPE_BRANCH 547 q.To.Sym = retTarget 548 } 549 q.Mark |= BRANCH 550 q.Spadj = autosize 551 552 case AADD: 553 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 554 p.Spadj = int32(-p.From.Offset) 555 } 556 557 case obj.AGETCALLERPC: 558 if cursym.Leaf() { 559 /* MOVD LR, Rd */ 560 p.As = AMOVD 561 p.From.Type = obj.TYPE_REG 562 p.From.Reg = REG_LR 563 } else { 564 /* MOVD (RSP), Rd */ 565 p.As = AMOVD 566 p.From.Type = obj.TYPE_MEM 567 p.From.Reg = REGSP 568 } 569 } 570 571 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 { 572 f := c.cursym.Func() 573 if f.FuncFlag&abi.FuncFlagSPWrite == 0 { 574 c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite 575 if ctxt.Debugvlog || !ctxt.IsAsm { 576 ctxt.Logf("auto-SPWRITE: %s\n", c.cursym.Name) 577 if !ctxt.IsAsm { 578 ctxt.Diag("invalid auto-SPWRITE in non-assembly") 579 ctxt.DiagFlush() 580 log.Fatalf("bad SPWRITE") 581 } 582 } 583 } 584 } 585 } 586 if wasSplit { 587 c.stacksplitPost(pLast, pPre, pPreempt, pCheck, autosize) // emit post part of split check 588 } 589 } 590 591 // stacksplitPre generates the function stack check prologue following 592 // Prog p (which should be the TEXT Prog). It returns one or two 593 // branch Progs that must be patched to jump to the morestack epilogue, 594 // and the Prog that starts the morestack check. 595 func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCheck *obj.Prog) { 596 if c.ctxt.Flag_maymorestack != "" { 597 // Save LR and REGCTXT 598 const frameSize = 16 599 p = c.ctxt.StartUnsafePoint(p, c.newprog) 600 // MOVD LR, -16(SP) 601 p = obj.Appendp(p, c.newprog) 602 p.As = AMOVD 603 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} 604 p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: -frameSize} 605 // MOVD $-16(SP), SP 606 p = obj.Appendp(p, c.newprog) 607 p.As = AMOVD 608 p.From = obj.Addr{Type: obj.TYPE_ADDR, Offset: -frameSize, Reg: REGSP} 609 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP} 610 p.Spadj = frameSize 611 // MOVD REGCTXT, 8(SP) 612 p = obj.Appendp(p, c.newprog) 613 p.As = AMOVD 614 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT} 615 p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8} 616 617 // BL maymorestack 618 p = obj.Appendp(p, c.newprog) 619 p.As = ABL 620 // See ../x86/obj6.go 621 sym := c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI()) 622 p.To = obj.Addr{Type: obj.TYPE_BRANCH, Sym: sym} 623 624 // Restore LR and REGCTXT 625 626 // MOVD REGCTXT, 8(SP) 627 p = obj.Appendp(p, c.newprog) 628 p.As = AMOVD 629 p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8} 630 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT} 631 // MOVD (SP), LR 632 p = obj.Appendp(p, c.newprog) 633 p.As = AMOVD 634 p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 0} 635 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} 636 // MOVD $16(SP), SP 637 p = obj.Appendp(p, c.newprog) 638 p.As = AMOVD 639 p.From = obj.Addr{Type: obj.TYPE_CONST, Reg: REGSP, Offset: frameSize} 640 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP} 641 p.Spadj = -frameSize 642 643 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 644 } 645 646 // MOVD g_stackguard(g), R3 647 p = obj.Appendp(p, c.newprog) 648 // Jump back to here after morestack returns. 649 pCheck = p 650 651 p.As = AMOVD 652 p.From.Type = obj.TYPE_MEM 653 p.From.Reg = REGG 654 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 655 if c.cursym.CFunc() { 656 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 657 } 658 p.To.Type = obj.TYPE_REG 659 p.To.Reg = REG_R3 660 661 // Mark the stack bound check and morestack call async nonpreemptible. 662 // If we get preempted here, when resumed the preemption request is 663 // cleared, but we'll still call morestack, which will double the stack 664 // unnecessarily. See issue #35470. 665 p = c.ctxt.StartUnsafePoint(p, c.newprog) 666 667 if framesize <= abi.StackSmall { 668 // small stack: SP < stackguard 669 // CMPUBGE stackguard, SP, label-of-call-to-morestack 670 671 p = obj.Appendp(p, c.newprog) 672 p.From.Type = obj.TYPE_REG 673 p.From.Reg = REG_R3 674 p.Reg = REGSP 675 p.As = ACMPUBGE 676 p.To.Type = obj.TYPE_BRANCH 677 678 return p, nil, pCheck 679 } 680 681 // large stack: SP-framesize < stackguard-StackSmall 682 683 offset := int64(framesize) - abi.StackSmall 684 if framesize > abi.StackBig { 685 // Such a large stack we need to protect against underflow. 686 // The runtime guarantees SP > objabi.StackBig, but 687 // framesize is large enough that SP-framesize may 688 // underflow, causing a direct comparison with the 689 // stack guard to incorrectly succeed. We explicitly 690 // guard against underflow. 691 // 692 // MOVD $(framesize-StackSmall), R4 693 // CMPUBLT SP, R4, label-of-call-to-morestack 694 695 p = obj.Appendp(p, c.newprog) 696 p.As = AMOVD 697 p.From.Type = obj.TYPE_CONST 698 p.From.Offset = offset 699 p.To.Type = obj.TYPE_REG 700 p.To.Reg = REG_R4 701 702 p = obj.Appendp(p, c.newprog) 703 pPreempt = p 704 p.As = ACMPUBLT 705 p.From.Type = obj.TYPE_REG 706 p.From.Reg = REGSP 707 p.Reg = REG_R4 708 p.To.Type = obj.TYPE_BRANCH 709 } 710 711 // Check against the stack guard. We've ensured this won't underflow. 712 // ADD $-(framesize-StackSmall), SP, R4 713 // CMPUBGE stackguard, R4, label-of-call-to-morestack 714 p = obj.Appendp(p, c.newprog) 715 p.As = AADD 716 p.From.Type = obj.TYPE_CONST 717 p.From.Offset = -offset 718 p.Reg = REGSP 719 p.To.Type = obj.TYPE_REG 720 p.To.Reg = REG_R4 721 722 p = obj.Appendp(p, c.newprog) 723 p.From.Type = obj.TYPE_REG 724 p.From.Reg = REG_R3 725 p.Reg = REG_R4 726 p.As = ACMPUBGE 727 p.To.Type = obj.TYPE_BRANCH 728 729 return p, pPreempt, pCheck 730 } 731 732 // stacksplitPost generates the function epilogue that calls morestack 733 // and returns the new last instruction in the function. 734 // 735 // p is the last Prog in the function. pPre and pPreempt, if non-nil, 736 // are the instructions that branch to the epilogue. This will fill in 737 // their branch targets. pCheck is the Prog that begins the stack check. 738 func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, framesize int32) *obj.Prog { 739 // Now we are at the end of the function, but logically 740 // we are still in function prologue. We need to fix the 741 // SP data and PCDATA. 742 spfix := obj.Appendp(p, c.newprog) 743 spfix.As = obj.ANOP 744 spfix.Spadj = -framesize 745 746 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog) 747 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog) 748 749 // MOVD LR, R5 750 p = obj.Appendp(pcdata, c.newprog) 751 pPre.To.SetTarget(p) 752 p.As = AMOVD 753 p.From.Type = obj.TYPE_REG 754 p.From.Reg = REG_LR 755 p.To.Type = obj.TYPE_REG 756 p.To.Reg = REG_R5 757 if pPreempt != nil { 758 pPreempt.To.SetTarget(p) 759 } 760 761 // BL runtime.morestack(SB) 762 p = obj.Appendp(p, c.newprog) 763 764 p.As = ABL 765 p.To.Type = obj.TYPE_BRANCH 766 if c.cursym.CFunc() { 767 p.To.Sym = c.ctxt.Lookup("runtime.morestackc") 768 } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { 769 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") 770 } else { 771 p.To.Sym = c.ctxt.Lookup("runtime.morestack") 772 } 773 774 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 775 776 // BR pCheck 777 p = obj.Appendp(p, c.newprog) 778 779 p.As = ABR 780 p.To.Type = obj.TYPE_BRANCH 781 p.To.SetTarget(pCheck) 782 return p 783 } 784 785 var unaryDst = map[obj.As]bool{ 786 ASTCK: true, 787 ASTCKC: true, 788 ASTCKE: true, 789 ASTCKF: true, 790 ANEG: true, 791 ANEGW: true, 792 AVONE: true, 793 AVZERO: true, 794 } 795 796 var Links390x = obj.LinkArch{ 797 Arch: sys.ArchS390X, 798 Init: buildop, 799 Preprocess: preprocess, 800 Assemble: spanz, 801 Progedit: progedit, 802 UnaryDst: unaryDst, 803 DWARFRegisters: S390XDWARFRegisters, 804 }