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