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