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