objz.go raw

   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  }
 736