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 30package s390x 31 32import ( 33 "cmd/internal/obj" 34 "cmd/internal/objabi" 35 "cmd/internal/sys" 36 "log" 37 "math" 38) 39 40func 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. 116func (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 207func 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.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 } else if c.cursym.Func().Text.Mark&LEAF == 0 { 362 // A very few functions that do not return to their caller 363 // (e.g. gogo) are not identified as leaves but still have 364 // no frame. 365 c.cursym.Func().Text.Mark |= LEAF 366 } 367 368 if c.cursym.Func().Text.Mark&LEAF != 0 { 369 c.cursym.Set(obj.AttrLeaf, true) 370 break 371 } 372 373 if c.cursym.Func().Text.From.Sym.Wrapper() { 374 // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame 375 // 376 // MOVD g_panic(g), R3 377 // CMP R3, $0 378 // BEQ end 379 // MOVD panic_argp(R3), R4 380 // ADD $(autosize+8), R1, R5 381 // CMP R4, R5 382 // BNE end 383 // ADD $8, R1, R6 384 // MOVD R6, panic_argp(R3) 385 // end: 386 // NOP 387 // 388 // The NOP is needed to give the jumps somewhere to land. 389 // It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes. 390 391 q = obj.Appendp(q, c.newprog) 392 393 q.As = AMOVD 394 q.From.Type = obj.TYPE_MEM 395 q.From.Reg = REGG 396 q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic 397 q.To.Type = obj.TYPE_REG 398 q.To.Reg = REG_R3 399 400 q = obj.Appendp(q, c.newprog) 401 q.As = ACMP 402 q.From.Type = obj.TYPE_REG 403 q.From.Reg = REG_R3 404 q.To.Type = obj.TYPE_CONST 405 q.To.Offset = 0 406 407 q = obj.Appendp(q, c.newprog) 408 q.As = ABEQ 409 q.To.Type = obj.TYPE_BRANCH 410 p1 := q 411 412 q = obj.Appendp(q, c.newprog) 413 q.As = AMOVD 414 q.From.Type = obj.TYPE_MEM 415 q.From.Reg = REG_R3 416 q.From.Offset = 0 // Panic.argp 417 q.To.Type = obj.TYPE_REG 418 q.To.Reg = REG_R4 419 420 q = obj.Appendp(q, c.newprog) 421 q.As = AADD 422 q.From.Type = obj.TYPE_CONST 423 q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize() 424 q.Reg = REGSP 425 q.To.Type = obj.TYPE_REG 426 q.To.Reg = REG_R5 427 428 q = obj.Appendp(q, c.newprog) 429 q.As = ACMP 430 q.From.Type = obj.TYPE_REG 431 q.From.Reg = REG_R4 432 q.To.Type = obj.TYPE_REG 433 q.To.Reg = REG_R5 434 435 q = obj.Appendp(q, c.newprog) 436 q.As = ABNE 437 q.To.Type = obj.TYPE_BRANCH 438 p2 := q 439 440 q = obj.Appendp(q, c.newprog) 441 q.As = AADD 442 q.From.Type = obj.TYPE_CONST 443 q.From.Offset = c.ctxt.FixedFrameSize() 444 q.Reg = REGSP 445 q.To.Type = obj.TYPE_REG 446 q.To.Reg = REG_R6 447 448 q = obj.Appendp(q, c.newprog) 449 q.As = AMOVD 450 q.From.Type = obj.TYPE_REG 451 q.From.Reg = REG_R6 452 q.To.Type = obj.TYPE_MEM 453 q.To.Reg = REG_R3 454 q.To.Offset = 0 // Panic.argp 455 456 q = obj.Appendp(q, c.newprog) 457 458 q.As = obj.ANOP 459 p1.To.SetTarget(q) 460 p2.To.SetTarget(q) 461 } 462 463 case obj.ARET: 464 retTarget := p.To.Sym 465 466 if c.cursym.Func().Text.Mark&LEAF != 0 { 467 if autosize == 0 { 468 p.As = ABR 469 p.From = obj.Addr{} 470 if retTarget == nil { 471 p.To.Type = obj.TYPE_REG 472 p.To.Reg = REG_LR 473 } else { 474 p.To.Type = obj.TYPE_BRANCH 475 p.To.Sym = retTarget 476 } 477 p.Mark |= BRANCH 478 break 479 } 480 481 p.As = AADD 482 p.From.Type = obj.TYPE_CONST 483 p.From.Offset = int64(autosize) 484 p.To.Type = obj.TYPE_REG 485 p.To.Reg = REGSP 486 p.Spadj = -autosize 487 488 q = obj.Appendp(p, c.newprog) 489 q.As = ABR 490 q.From = obj.Addr{} 491 if retTarget == nil { 492 q.To.Type = obj.TYPE_REG 493 q.To.Reg = REG_LR 494 } else { 495 q.To.Type = obj.TYPE_BRANCH 496 q.To.Sym = retTarget 497 } 498 q.Mark |= BRANCH 499 q.Spadj = autosize 500 break 501 } 502 503 p.As = AMOVD 504 p.From.Type = obj.TYPE_MEM 505 p.From.Reg = REGSP 506 p.From.Offset = 0 507 p.To = obj.Addr{ 508 Type: obj.TYPE_REG, 509 Reg: REG_LR, 510 } 511 512 q = p 513 514 if autosize != 0 { 515 q = obj.Appendp(q, c.newprog) 516 q.As = AADD 517 q.From.Type = obj.TYPE_CONST 518 q.From.Offset = int64(autosize) 519 q.To.Type = obj.TYPE_REG 520 q.To.Reg = REGSP 521 q.Spadj = -autosize 522 } 523 524 q = obj.Appendp(q, c.newprog) 525 q.As = ABR 526 q.From = obj.Addr{} 527 if retTarget == nil { 528 q.To.Type = obj.TYPE_REG 529 q.To.Reg = REG_LR 530 } else { 531 q.To.Type = obj.TYPE_BRANCH 532 q.To.Sym = retTarget 533 } 534 q.Mark |= BRANCH 535 q.Spadj = autosize 536 537 case AADD: 538 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST { 539 p.Spadj = int32(-p.From.Offset) 540 } 541 542 case obj.AGETCALLERPC: 543 if cursym.Leaf() { 544 /* MOVD LR, Rd */ 545 p.As = AMOVD 546 p.From.Type = obj.TYPE_REG 547 p.From.Reg = REG_LR 548 } else { 549 /* MOVD (RSP), Rd */ 550 p.As = AMOVD 551 p.From.Type = obj.TYPE_MEM 552 p.From.Reg = REGSP 553 } 554 } 555 556 if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 { 557 f := c.cursym.Func() 558 if f.FuncFlag&objabi.FuncFlag_SPWRITE == 0 { 559 c.cursym.Func().FuncFlag |= objabi.FuncFlag_SPWRITE 560 if ctxt.Debugvlog || !ctxt.IsAsm { 561 ctxt.Logf("auto-SPWRITE: %s\n", c.cursym.Name) 562 if !ctxt.IsAsm { 563 ctxt.Diag("invalid auto-SPWRITE in non-assembly") 564 ctxt.DiagFlush() 565 log.Fatalf("bad SPWRITE") 566 } 567 } 568 } 569 } 570 } 571 if wasSplit { 572 c.stacksplitPost(pLast, pPre, pPreempt, pCheck, autosize) // emit post part of split check 573 } 574} 575 576// stacksplitPre generates the function stack check prologue following 577// Prog p (which should be the TEXT Prog). It returns one or two 578// branch Progs that must be patched to jump to the morestack epilogue, 579// and the Prog that starts the morestack check. 580func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCheck *obj.Prog) { 581 if c.ctxt.Flag_maymorestack != "" { 582 // Save LR and REGCTXT 583 const frameSize = 16 584 p = c.ctxt.StartUnsafePoint(p, c.newprog) 585 // MOVD LR, -16(SP) 586 p = obj.Appendp(p, c.newprog) 587 p.As = AMOVD 588 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} 589 p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: -frameSize} 590 // MOVD $-16(SP), SP 591 p = obj.Appendp(p, c.newprog) 592 p.As = AMOVD 593 p.From = obj.Addr{Type: obj.TYPE_ADDR, Offset: -frameSize, Reg: REGSP} 594 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP} 595 p.Spadj = frameSize 596 // MOVD REGCTXT, 8(SP) 597 p = obj.Appendp(p, c.newprog) 598 p.As = AMOVD 599 p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT} 600 p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8} 601 602 // BL maymorestack 603 p = obj.Appendp(p, c.newprog) 604 p.As = ABL 605 // See ../x86/obj6.go 606 sym := c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI()) 607 p.To = obj.Addr{Type: obj.TYPE_BRANCH, Sym: sym} 608 609 // Restore LR and REGCTXT 610 611 // MOVD REGCTXT, 8(SP) 612 p = obj.Appendp(p, c.newprog) 613 p.As = AMOVD 614 p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8} 615 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT} 616 // MOVD (SP), LR 617 p = obj.Appendp(p, c.newprog) 618 p.As = AMOVD 619 p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 0} 620 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} 621 // MOVD $16(SP), SP 622 p = obj.Appendp(p, c.newprog) 623 p.As = AMOVD 624 p.From = obj.Addr{Type: obj.TYPE_CONST, Reg: REGSP, Offset: frameSize} 625 p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP} 626 p.Spadj = -frameSize 627 628 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 629 } 630 631 // MOVD g_stackguard(g), R3 632 p = obj.Appendp(p, c.newprog) 633 // Jump back to here after morestack returns. 634 pCheck = p 635 636 p.As = AMOVD 637 p.From.Type = obj.TYPE_MEM 638 p.From.Reg = REGG 639 p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0 640 if c.cursym.CFunc() { 641 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 642 } 643 p.To.Type = obj.TYPE_REG 644 p.To.Reg = REG_R3 645 646 // Mark the stack bound check and morestack call async nonpreemptible. 647 // If we get preempted here, when resumed the preemption request is 648 // cleared, but we'll still call morestack, which will double the stack 649 // unnecessarily. See issue #35470. 650 p = c.ctxt.StartUnsafePoint(p, c.newprog) 651 652 if framesize <= objabi.StackSmall { 653 // small stack: SP < stackguard 654 // CMPUBGE stackguard, SP, label-of-call-to-morestack 655 656 p = obj.Appendp(p, c.newprog) 657 p.From.Type = obj.TYPE_REG 658 p.From.Reg = REG_R3 659 p.Reg = REGSP 660 p.As = ACMPUBGE 661 p.To.Type = obj.TYPE_BRANCH 662 663 return p, nil, pCheck 664 } 665 666 // large stack: SP-framesize < stackguard-StackSmall 667 668 offset := int64(framesize) - objabi.StackSmall 669 if framesize > objabi.StackBig { 670 // Such a large stack we need to protect against underflow. 671 // The runtime guarantees SP > objabi.StackBig, but 672 // framesize is large enough that SP-framesize may 673 // underflow, causing a direct comparison with the 674 // stack guard to incorrectly succeed. We explicitly 675 // guard against underflow. 676 // 677 // MOVD $(framesize-StackSmall), R4 678 // CMPUBLT SP, R4, label-of-call-to-morestack 679 680 p = obj.Appendp(p, c.newprog) 681 p.As = AMOVD 682 p.From.Type = obj.TYPE_CONST 683 p.From.Offset = offset 684 p.To.Type = obj.TYPE_REG 685 p.To.Reg = REG_R4 686 687 p = obj.Appendp(p, c.newprog) 688 pPreempt = p 689 p.As = ACMPUBLT 690 p.From.Type = obj.TYPE_REG 691 p.From.Reg = REGSP 692 p.Reg = REG_R4 693 p.To.Type = obj.TYPE_BRANCH 694 } 695 696 // Check against the stack guard. We've ensured this won't underflow. 697 // ADD $-(framesize-StackSmall), SP, R4 698 // CMPUBGE stackguard, R4, label-of-call-to-morestack 699 p = obj.Appendp(p, c.newprog) 700 p.As = AADD 701 p.From.Type = obj.TYPE_CONST 702 p.From.Offset = -offset 703 p.Reg = REGSP 704 p.To.Type = obj.TYPE_REG 705 p.To.Reg = REG_R4 706 707 p = obj.Appendp(p, c.newprog) 708 p.From.Type = obj.TYPE_REG 709 p.From.Reg = REG_R3 710 p.Reg = REG_R4 711 p.As = ACMPUBGE 712 p.To.Type = obj.TYPE_BRANCH 713 714 return p, pPreempt, pCheck 715} 716 717// stacksplitPost generates the function epilogue that calls morestack 718// and returns the new last instruction in the function. 719// 720// p is the last Prog in the function. pPre and pPreempt, if non-nil, 721// are the instructions that branch to the epilogue. This will fill in 722// their branch targets. pCheck is the Prog that begins the stack check. 723func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, framesize int32) *obj.Prog { 724 // Now we are at the end of the function, but logically 725 // we are still in function prologue. We need to fix the 726 // SP data and PCDATA. 727 spfix := obj.Appendp(p, c.newprog) 728 spfix.As = obj.ANOP 729 spfix.Spadj = -framesize 730 731 pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog) 732 pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog) 733 734 // MOVD LR, R5 735 p = obj.Appendp(pcdata, c.newprog) 736 pPre.To.SetTarget(p) 737 p.As = AMOVD 738 p.From.Type = obj.TYPE_REG 739 p.From.Reg = REG_LR 740 p.To.Type = obj.TYPE_REG 741 p.To.Reg = REG_R5 742 if pPreempt != nil { 743 pPreempt.To.SetTarget(p) 744 } 745 746 // BL runtime.morestack(SB) 747 p = obj.Appendp(p, c.newprog) 748 749 p.As = ABL 750 p.To.Type = obj.TYPE_BRANCH 751 if c.cursym.CFunc() { 752 p.To.Sym = c.ctxt.Lookup("runtime.morestackc") 753 } else if !c.cursym.Func().Text.From.Sym.NeedCtxt() { 754 p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt") 755 } else { 756 p.To.Sym = c.ctxt.Lookup("runtime.morestack") 757 } 758 759 p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) 760 761 // BR pCheck 762 p = obj.Appendp(p, c.newprog) 763 764 p.As = ABR 765 p.To.Type = obj.TYPE_BRANCH 766 p.To.SetTarget(pCheck) 767 return p 768} 769 770var unaryDst = map[obj.As]bool{ 771 ASTCK: true, 772 ASTCKC: true, 773 ASTCKE: true, 774 ASTCKF: true, 775 ANEG: true, 776 ANEGW: true, 777 AVONE: true, 778 AVZERO: true, 779} 780 781var Links390x = obj.LinkArch{ 782 Arch: sys.ArchS390X, 783 Init: buildop, 784 Preprocess: preprocess, 785 Assemble: spanz, 786 Progedit: progedit, 787 UnaryDst: unaryDst, 788 DWARFRegisters: S390XDWARFRegisters, 789} 790