github.com/sanprasirt/go@v0.0.0-20170607001320-a027466e4b6d/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  	"cmd/internal/obj"
    34  	"cmd/internal/objabi"
    35  	"cmd/internal/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  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   126  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   127  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   128  		if p.To.Type != obj.TYPE_REG || p.As != AMOVD {
   129  			c.ctxt.Diag("do not know how to handle LEA-type insn to non-register in %v with -dynlink", p)
   130  		}
   131  		p.From.Type = obj.TYPE_MEM
   132  		p.From.Name = obj.NAME_GOTREF
   133  		q := p
   134  		if p.From.Offset != 0 {
   135  			q = obj.Appendp(p, c.newprog)
   136  			q.As = AADD
   137  			q.From.Type = obj.TYPE_CONST
   138  			q.From.Offset = p.From.Offset
   139  			q.To = p.To
   140  			p.From.Offset = 0
   141  		}
   142  	}
   143  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   144  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   145  	}
   146  	var source *obj.Addr
   147  	// MOVD sym, Ry becomes MOVD sym@GOT, REGTMP; MOVD (REGTMP), Ry
   148  	// MOVD Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   149  	// An addition may be inserted between the two MOVs if there is an offset.
   150  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   151  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   152  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   153  		}
   154  		source = &p.From
   155  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   156  		source = &p.To
   157  	} else {
   158  		return
   159  	}
   160  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   161  		return
   162  	}
   163  	if source.Sym.Type == objabi.STLSBSS {
   164  		return
   165  	}
   166  	if source.Type != obj.TYPE_MEM {
   167  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   168  	}
   169  	p1 := obj.Appendp(p, c.newprog)
   170  	p2 := obj.Appendp(p1, c.newprog)
   171  
   172  	p1.As = AMOVD
   173  	p1.From.Type = obj.TYPE_MEM
   174  	p1.From.Sym = source.Sym
   175  	p1.From.Name = obj.NAME_GOTREF
   176  	p1.To.Type = obj.TYPE_REG
   177  	p1.To.Reg = REGTMP
   178  
   179  	p2.As = p.As
   180  	p2.From = p.From
   181  	p2.To = p.To
   182  	if p.From.Name == obj.NAME_EXTERN {
   183  		p2.From.Reg = REGTMP
   184  		p2.From.Name = obj.NAME_NONE
   185  		p2.From.Sym = nil
   186  	} else if p.To.Name == obj.NAME_EXTERN {
   187  		p2.To.Reg = REGTMP
   188  		p2.To.Name = obj.NAME_NONE
   189  		p2.To.Sym = nil
   190  	} else {
   191  		return
   192  	}
   193  	obj.Nopout(p)
   194  }
   195  
   196  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   197  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
   198  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   199  		return
   200  	}
   201  
   202  	c := ctxtz{ctxt: ctxt, cursym: cursym, newprog: newprog}
   203  
   204  	p := c.cursym.Func.Text
   205  	textstksiz := p.To.Offset
   206  	if textstksiz == -8 {
   207  		// Compatibility hack.
   208  		p.From.Sym.Set(obj.AttrNoFrame, true)
   209  		textstksiz = 0
   210  	}
   211  	if textstksiz%8 != 0 {
   212  		c.ctxt.Diag("frame size %d not a multiple of 8", textstksiz)
   213  	}
   214  	if p.From.Sym.NoFrame() {
   215  		if textstksiz != 0 {
   216  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   217  		}
   218  	}
   219  
   220  	c.cursym.Func.Args = p.To.Val.(int32)
   221  	c.cursym.Func.Locals = int32(textstksiz)
   222  
   223  	/*
   224  	 * find leaf subroutines
   225  	 * strip NOPs
   226  	 * expand RET
   227  	 */
   228  
   229  	var q *obj.Prog
   230  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   231  		switch p.As {
   232  		case obj.ATEXT:
   233  			q = p
   234  			p.Mark |= LEAF
   235  
   236  		case ABL, ABCL:
   237  			q = p
   238  			c.cursym.Func.Text.Mark &^= LEAF
   239  			fallthrough
   240  
   241  		case ABC,
   242  			ABEQ,
   243  			ABGE,
   244  			ABGT,
   245  			ABLE,
   246  			ABLT,
   247  			ABLEU,
   248  			ABLTU,
   249  			ABNE,
   250  			ABR,
   251  			ABVC,
   252  			ABVS,
   253  			ACMPBEQ,
   254  			ACMPBGE,
   255  			ACMPBGT,
   256  			ACMPBLE,
   257  			ACMPBLT,
   258  			ACMPBNE,
   259  			ACMPUBEQ,
   260  			ACMPUBGE,
   261  			ACMPUBGT,
   262  			ACMPUBLE,
   263  			ACMPUBLT,
   264  			ACMPUBNE:
   265  			q = p
   266  			p.Mark |= BRANCH
   267  			if p.Pcond != nil {
   268  				q := p.Pcond
   269  				for q.As == obj.ANOP {
   270  					q = q.Link
   271  					p.Pcond = q
   272  				}
   273  			}
   274  
   275  		case obj.ANOP:
   276  			q.Link = p.Link /* q is non-nop */
   277  			p.Link.Mark |= p.Mark
   278  
   279  		default:
   280  			q = p
   281  		}
   282  	}
   283  
   284  	autosize := int32(0)
   285  	var pLast *obj.Prog
   286  	var pPre *obj.Prog
   287  	var pPreempt *obj.Prog
   288  	wasSplit := false
   289  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   290  		pLast = p
   291  		switch p.As {
   292  		case obj.ATEXT:
   293  			autosize = int32(textstksiz)
   294  
   295  			if p.Mark&LEAF != 0 && autosize == 0 {
   296  				// A leaf function with no locals has no frame.
   297  				p.From.Sym.Set(obj.AttrNoFrame, true)
   298  			}
   299  
   300  			if !p.From.Sym.NoFrame() {
   301  				// If there is a stack frame at all, it includes
   302  				// space to save the LR.
   303  				autosize += int32(c.ctxt.FixedFrameSize())
   304  			}
   305  
   306  			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
   307  				// A leaf function with a small stack can be marked
   308  				// NOSPLIT, avoiding a stack check.
   309  				p.From.Sym.Set(obj.AttrNoSplit, true)
   310  			}
   311  
   312  			p.To.Offset = int64(autosize)
   313  
   314  			q := p
   315  
   316  			if !p.From.Sym.NoSplit() {
   317  				p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
   318  				pPre = p
   319  				wasSplit = true //need post part of split
   320  			}
   321  
   322  			if autosize != 0 {
   323  				// Make sure to save link register for non-empty frame, even if
   324  				// it is a leaf function, so that traceback works.
   325  				// Store link register before decrementing SP, so if a signal comes
   326  				// during the execution of the function prologue, the traceback
   327  				// code will not see a half-updated stack frame.
   328  				q = obj.Appendp(p, c.newprog)
   329  				q.As = AMOVD
   330  				q.From.Type = obj.TYPE_REG
   331  				q.From.Reg = REG_LR
   332  				q.To.Type = obj.TYPE_MEM
   333  				q.To.Reg = REGSP
   334  				q.To.Offset = int64(-autosize)
   335  
   336  				q = obj.Appendp(q, c.newprog)
   337  				q.As = AMOVD
   338  				q.From.Type = obj.TYPE_ADDR
   339  				q.From.Offset = int64(-autosize)
   340  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   341  				q.To.Type = obj.TYPE_REG
   342  				q.To.Reg = REGSP
   343  				q.Spadj = autosize
   344  			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   345  				// A very few functions that do not return to their caller
   346  				// (e.g. gogo) are not identified as leaves but still have
   347  				// no frame.
   348  				c.cursym.Func.Text.Mark |= LEAF
   349  			}
   350  
   351  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   352  				c.cursym.Set(obj.AttrLeaf, true)
   353  				break
   354  			}
   355  
   356  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   357  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   358  				//
   359  				//	MOVD g_panic(g), R3
   360  				//	CMP R3, $0
   361  				//	BEQ end
   362  				//	MOVD panic_argp(R3), R4
   363  				//	ADD $(autosize+8), R1, R5
   364  				//	CMP R4, R5
   365  				//	BNE end
   366  				//	ADD $8, R1, R6
   367  				//	MOVD R6, panic_argp(R3)
   368  				// end:
   369  				//	NOP
   370  				//
   371  				// The NOP is needed to give the jumps somewhere to land.
   372  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   373  
   374  				q = obj.Appendp(q, c.newprog)
   375  
   376  				q.As = AMOVD
   377  				q.From.Type = obj.TYPE_MEM
   378  				q.From.Reg = REGG
   379  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   380  				q.To.Type = obj.TYPE_REG
   381  				q.To.Reg = REG_R3
   382  
   383  				q = obj.Appendp(q, c.newprog)
   384  				q.As = ACMP
   385  				q.From.Type = obj.TYPE_REG
   386  				q.From.Reg = REG_R3
   387  				q.To.Type = obj.TYPE_CONST
   388  				q.To.Offset = 0
   389  
   390  				q = obj.Appendp(q, c.newprog)
   391  				q.As = ABEQ
   392  				q.To.Type = obj.TYPE_BRANCH
   393  				p1 := q
   394  
   395  				q = obj.Appendp(q, c.newprog)
   396  				q.As = AMOVD
   397  				q.From.Type = obj.TYPE_MEM
   398  				q.From.Reg = REG_R3
   399  				q.From.Offset = 0 // Panic.argp
   400  				q.To.Type = obj.TYPE_REG
   401  				q.To.Reg = REG_R4
   402  
   403  				q = obj.Appendp(q, c.newprog)
   404  				q.As = AADD
   405  				q.From.Type = obj.TYPE_CONST
   406  				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   407  				q.Reg = REGSP
   408  				q.To.Type = obj.TYPE_REG
   409  				q.To.Reg = REG_R5
   410  
   411  				q = obj.Appendp(q, c.newprog)
   412  				q.As = ACMP
   413  				q.From.Type = obj.TYPE_REG
   414  				q.From.Reg = REG_R4
   415  				q.To.Type = obj.TYPE_REG
   416  				q.To.Reg = REG_R5
   417  
   418  				q = obj.Appendp(q, c.newprog)
   419  				q.As = ABNE
   420  				q.To.Type = obj.TYPE_BRANCH
   421  				p2 := q
   422  
   423  				q = obj.Appendp(q, c.newprog)
   424  				q.As = AADD
   425  				q.From.Type = obj.TYPE_CONST
   426  				q.From.Offset = c.ctxt.FixedFrameSize()
   427  				q.Reg = REGSP
   428  				q.To.Type = obj.TYPE_REG
   429  				q.To.Reg = REG_R6
   430  
   431  				q = obj.Appendp(q, c.newprog)
   432  				q.As = AMOVD
   433  				q.From.Type = obj.TYPE_REG
   434  				q.From.Reg = REG_R6
   435  				q.To.Type = obj.TYPE_MEM
   436  				q.To.Reg = REG_R3
   437  				q.To.Offset = 0 // Panic.argp
   438  
   439  				q = obj.Appendp(q, c.newprog)
   440  
   441  				q.As = obj.ANOP
   442  				p1.Pcond = q
   443  				p2.Pcond = q
   444  			}
   445  
   446  		case obj.ARET:
   447  			retTarget := p.To.Sym
   448  
   449  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   450  				if autosize == 0 {
   451  					p.As = ABR
   452  					p.From = obj.Addr{}
   453  					if retTarget == nil {
   454  						p.To.Type = obj.TYPE_REG
   455  						p.To.Reg = REG_LR
   456  					} else {
   457  						p.To.Type = obj.TYPE_BRANCH
   458  						p.To.Sym = retTarget
   459  					}
   460  					p.Mark |= BRANCH
   461  					break
   462  				}
   463  
   464  				p.As = AADD
   465  				p.From.Type = obj.TYPE_CONST
   466  				p.From.Offset = int64(autosize)
   467  				p.To.Type = obj.TYPE_REG
   468  				p.To.Reg = REGSP
   469  				p.Spadj = -autosize
   470  
   471  				q = obj.Appendp(p, c.newprog)
   472  				q.As = ABR
   473  				q.From = obj.Addr{}
   474  				q.To.Type = obj.TYPE_REG
   475  				q.To.Reg = REG_LR
   476  				q.Mark |= BRANCH
   477  				q.Spadj = autosize
   478  				break
   479  			}
   480  
   481  			p.As = AMOVD
   482  			p.From.Type = obj.TYPE_MEM
   483  			p.From.Reg = REGSP
   484  			p.From.Offset = 0
   485  			p.To.Type = obj.TYPE_REG
   486  			p.To.Reg = REG_LR
   487  
   488  			q = p
   489  
   490  			if autosize != 0 {
   491  				q = obj.Appendp(q, c.newprog)
   492  				q.As = AADD
   493  				q.From.Type = obj.TYPE_CONST
   494  				q.From.Offset = int64(autosize)
   495  				q.To.Type = obj.TYPE_REG
   496  				q.To.Reg = REGSP
   497  				q.Spadj = -autosize
   498  			}
   499  
   500  			q = obj.Appendp(q, c.newprog)
   501  			q.As = ABR
   502  			q.From = obj.Addr{}
   503  			if retTarget == nil {
   504  				q.To.Type = obj.TYPE_REG
   505  				q.To.Reg = REG_LR
   506  			} else {
   507  				q.To.Type = obj.TYPE_BRANCH
   508  				q.To.Sym = retTarget
   509  			}
   510  			q.Mark |= BRANCH
   511  			q.Spadj = autosize
   512  
   513  		case AADD:
   514  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   515  				p.Spadj = int32(-p.From.Offset)
   516  			}
   517  		}
   518  	}
   519  	if wasSplit {
   520  		c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
   521  	}
   522  }
   523  
   524  func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   525  	var q *obj.Prog
   526  
   527  	// MOVD	g_stackguard(g), R3
   528  	p = obj.Appendp(p, c.newprog)
   529  
   530  	p.As = AMOVD
   531  	p.From.Type = obj.TYPE_MEM
   532  	p.From.Reg = REGG
   533  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   534  	if c.cursym.CFunc() {
   535  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   536  	}
   537  	p.To.Type = obj.TYPE_REG
   538  	p.To.Reg = REG_R3
   539  
   540  	q = nil
   541  	if framesize <= objabi.StackSmall {
   542  		// small stack: SP < stackguard
   543  		//	CMP	stackguard, SP
   544  
   545  		//p.To.Type = obj.TYPE_REG
   546  		//p.To.Reg = REGSP
   547  
   548  		// q1: BLT	done
   549  
   550  		p = obj.Appendp(p, c.newprog)
   551  		//q1 = p
   552  		p.From.Type = obj.TYPE_REG
   553  		p.From.Reg = REG_R3
   554  		p.Reg = REGSP
   555  		p.As = ACMPUBGE
   556  		p.To.Type = obj.TYPE_BRANCH
   557  		//p = obj.Appendp(ctxt, p)
   558  
   559  		//p.As = ACMPU
   560  		//p.From.Type = obj.TYPE_REG
   561  		//p.From.Reg = REG_R3
   562  		//p.To.Type = obj.TYPE_REG
   563  		//p.To.Reg = REGSP
   564  
   565  		//p = obj.Appendp(ctxt, p)
   566  		//p.As = ABGE
   567  		//p.To.Type = obj.TYPE_BRANCH
   568  
   569  	} else if framesize <= objabi.StackBig {
   570  		// large stack: SP-framesize < stackguard-StackSmall
   571  		//	ADD $-(framesize-StackSmall), SP, R4
   572  		//	CMP stackguard, R4
   573  		p = obj.Appendp(p, c.newprog)
   574  
   575  		p.As = AADD
   576  		p.From.Type = obj.TYPE_CONST
   577  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   578  		p.Reg = REGSP
   579  		p.To.Type = obj.TYPE_REG
   580  		p.To.Reg = REG_R4
   581  
   582  		p = obj.Appendp(p, c.newprog)
   583  		p.From.Type = obj.TYPE_REG
   584  		p.From.Reg = REG_R3
   585  		p.Reg = REG_R4
   586  		p.As = ACMPUBGE
   587  		p.To.Type = obj.TYPE_BRANCH
   588  
   589  	} else {
   590  		// Such a large stack we need to protect against wraparound.
   591  		// If SP is close to zero:
   592  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   593  		// The +StackGuard on both sides is required to keep the left side positive:
   594  		// SP is allowed to be slightly below stackguard. See stack.h.
   595  		//
   596  		// Preemption sets stackguard to StackPreempt, a very large value.
   597  		// That breaks the math above, so we have to check for that explicitly.
   598  		//	// stackguard is R3
   599  		//	CMP	R3, $StackPreempt
   600  		//	BEQ	label-of-call-to-morestack
   601  		//	ADD	$StackGuard, SP, R4
   602  		//	SUB	R3, R4
   603  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   604  		//	CMPUBGE	TEMP, R4
   605  		p = obj.Appendp(p, c.newprog)
   606  
   607  		p.As = ACMP
   608  		p.From.Type = obj.TYPE_REG
   609  		p.From.Reg = REG_R3
   610  		p.To.Type = obj.TYPE_CONST
   611  		p.To.Offset = objabi.StackPreempt
   612  
   613  		p = obj.Appendp(p, c.newprog)
   614  		q = p
   615  		p.As = ABEQ
   616  		p.To.Type = obj.TYPE_BRANCH
   617  
   618  		p = obj.Appendp(p, c.newprog)
   619  		p.As = AADD
   620  		p.From.Type = obj.TYPE_CONST
   621  		p.From.Offset = objabi.StackGuard
   622  		p.Reg = REGSP
   623  		p.To.Type = obj.TYPE_REG
   624  		p.To.Reg = REG_R4
   625  
   626  		p = obj.Appendp(p, c.newprog)
   627  		p.As = ASUB
   628  		p.From.Type = obj.TYPE_REG
   629  		p.From.Reg = REG_R3
   630  		p.To.Type = obj.TYPE_REG
   631  		p.To.Reg = REG_R4
   632  
   633  		p = obj.Appendp(p, c.newprog)
   634  		p.As = AMOVD
   635  		p.From.Type = obj.TYPE_CONST
   636  		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
   637  		p.To.Type = obj.TYPE_REG
   638  		p.To.Reg = REGTMP
   639  
   640  		p = obj.Appendp(p, c.newprog)
   641  		p.From.Type = obj.TYPE_REG
   642  		p.From.Reg = REGTMP
   643  		p.Reg = REG_R4
   644  		p.As = ACMPUBGE
   645  		p.To.Type = obj.TYPE_BRANCH
   646  	}
   647  
   648  	return p, q
   649  }
   650  
   651  func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
   652  	// Now we are at the end of the function, but logically
   653  	// we are still in function prologue. We need to fix the
   654  	// SP data and PCDATA.
   655  	spfix := obj.Appendp(p, c.newprog)
   656  	spfix.As = obj.ANOP
   657  	spfix.Spadj = -framesize
   658  
   659  	pcdata := obj.Appendp(spfix, c.newprog)
   660  	pcdata.Pos = c.cursym.Func.Text.Pos
   661  	pcdata.As = obj.APCDATA
   662  	pcdata.From.Type = obj.TYPE_CONST
   663  	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
   664  	pcdata.To.Type = obj.TYPE_CONST
   665  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   666  
   667  	// MOVD	LR, R5
   668  	p = obj.Appendp(pcdata, c.newprog)
   669  	pPre.Pcond = p
   670  	p.As = AMOVD
   671  	p.From.Type = obj.TYPE_REG
   672  	p.From.Reg = REG_LR
   673  	p.To.Type = obj.TYPE_REG
   674  	p.To.Reg = REG_R5
   675  	if pPreempt != nil {
   676  		pPreempt.Pcond = p
   677  	}
   678  
   679  	// BL	runtime.morestack(SB)
   680  	p = obj.Appendp(p, c.newprog)
   681  
   682  	p.As = ABL
   683  	p.To.Type = obj.TYPE_BRANCH
   684  	if c.cursym.CFunc() {
   685  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   686  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
   687  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   688  	} else {
   689  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   690  	}
   691  
   692  	// BR	start
   693  	p = obj.Appendp(p, c.newprog)
   694  
   695  	p.As = ABR
   696  	p.To.Type = obj.TYPE_BRANCH
   697  	p.Pcond = c.cursym.Func.Text.Link
   698  	return p
   699  }
   700  
   701  var unaryDst = map[obj.As]bool{
   702  	ASTCK:  true,
   703  	ASTCKC: true,
   704  	ASTCKE: true,
   705  	ASTCKF: true,
   706  	ANEG:   true,
   707  	ANEGW:  true,
   708  	AVONE:  true,
   709  	AVZERO: true,
   710  }
   711  
   712  var Links390x = obj.LinkArch{
   713  	Arch:       sys.ArchS390X,
   714  	Init:       buildop,
   715  	Preprocess: preprocess,
   716  	Assemble:   spanz,
   717  	Progedit:   progedit,
   718  	UnaryDst:   unaryDst,
   719  }