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