github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/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  	"github.com/gagliardetto/golang-go/cmd/internal/obj"
    34  	"github.com/gagliardetto/golang-go/cmd/internal/objabi"
    35  	"github.com/gagliardetto/golang-go/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  	// 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  			if p.Pcond != nil {
   287  				q := p.Pcond
   288  				for q.As == obj.ANOP {
   289  					q = q.Link
   290  					p.Pcond = q
   291  				}
   292  			}
   293  
   294  		case obj.ANOP:
   295  			q.Link = p.Link /* q is non-nop */
   296  			p.Link.Mark |= p.Mark
   297  
   298  		default:
   299  			q = p
   300  		}
   301  	}
   302  
   303  	autosize := int32(0)
   304  	var pLast *obj.Prog
   305  	var pPre *obj.Prog
   306  	var pPreempt *obj.Prog
   307  	wasSplit := false
   308  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   309  		pLast = p
   310  		switch p.As {
   311  		case obj.ATEXT:
   312  			autosize = int32(textstksiz)
   313  
   314  			if p.Mark&LEAF != 0 && autosize == 0 {
   315  				// A leaf function with no locals has no frame.
   316  				p.From.Sym.Set(obj.AttrNoFrame, true)
   317  			}
   318  
   319  			if !p.From.Sym.NoFrame() {
   320  				// If there is a stack frame at all, it includes
   321  				// space to save the LR.
   322  				autosize += int32(c.ctxt.FixedFrameSize())
   323  			}
   324  
   325  			if p.Mark&LEAF != 0 && autosize < objabi.StackSmall {
   326  				// A leaf function with a small stack can be marked
   327  				// NOSPLIT, avoiding a stack check.
   328  				p.From.Sym.Set(obj.AttrNoSplit, true)
   329  			}
   330  
   331  			p.To.Offset = int64(autosize)
   332  
   333  			q := p
   334  
   335  			if !p.From.Sym.NoSplit() {
   336  				p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
   337  				pPre = p
   338  				p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   339  				wasSplit = true //need post part of split
   340  			}
   341  
   342  			if autosize != 0 {
   343  				// Make sure to save link register for non-empty frame, even if
   344  				// it is a leaf function, so that traceback works.
   345  				// Store link register before decrementing SP, so if a signal comes
   346  				// during the execution of the function prologue, the traceback
   347  				// code will not see a half-updated stack frame.
   348  				// This sequence is not async preemptible, as if we open a frame
   349  				// at the current SP, it will clobber the saved LR.
   350  				q = c.ctxt.StartUnsafePoint(p, c.newprog)
   351  
   352  				q = obj.Appendp(q, c.newprog)
   353  				q.As = AMOVD
   354  				q.From.Type = obj.TYPE_REG
   355  				q.From.Reg = REG_LR
   356  				q.To.Type = obj.TYPE_MEM
   357  				q.To.Reg = REGSP
   358  				q.To.Offset = int64(-autosize)
   359  
   360  				q = obj.Appendp(q, c.newprog)
   361  				q.As = AMOVD
   362  				q.From.Type = obj.TYPE_ADDR
   363  				q.From.Offset = int64(-autosize)
   364  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   365  				q.To.Type = obj.TYPE_REG
   366  				q.To.Reg = REGSP
   367  				q.Spadj = autosize
   368  
   369  				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
   370  			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   371  				// A very few functions that do not return to their caller
   372  				// (e.g. gogo) are not identified as leaves but still have
   373  				// no frame.
   374  				c.cursym.Func.Text.Mark |= LEAF
   375  			}
   376  
   377  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   378  				c.cursym.Set(obj.AttrLeaf, true)
   379  				break
   380  			}
   381  
   382  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   383  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   384  				//
   385  				//	MOVD g_panic(g), R3
   386  				//	CMP R3, $0
   387  				//	BEQ end
   388  				//	MOVD panic_argp(R3), R4
   389  				//	ADD $(autosize+8), R1, R5
   390  				//	CMP R4, R5
   391  				//	BNE end
   392  				//	ADD $8, R1, R6
   393  				//	MOVD R6, panic_argp(R3)
   394  				// end:
   395  				//	NOP
   396  				//
   397  				// The NOP is needed to give the jumps somewhere to land.
   398  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   399  
   400  				q = obj.Appendp(q, c.newprog)
   401  
   402  				q.As = AMOVD
   403  				q.From.Type = obj.TYPE_MEM
   404  				q.From.Reg = REGG
   405  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   406  				q.To.Type = obj.TYPE_REG
   407  				q.To.Reg = REG_R3
   408  
   409  				q = obj.Appendp(q, c.newprog)
   410  				q.As = ACMP
   411  				q.From.Type = obj.TYPE_REG
   412  				q.From.Reg = REG_R3
   413  				q.To.Type = obj.TYPE_CONST
   414  				q.To.Offset = 0
   415  
   416  				q = obj.Appendp(q, c.newprog)
   417  				q.As = ABEQ
   418  				q.To.Type = obj.TYPE_BRANCH
   419  				p1 := q
   420  
   421  				q = obj.Appendp(q, c.newprog)
   422  				q.As = AMOVD
   423  				q.From.Type = obj.TYPE_MEM
   424  				q.From.Reg = REG_R3
   425  				q.From.Offset = 0 // Panic.argp
   426  				q.To.Type = obj.TYPE_REG
   427  				q.To.Reg = REG_R4
   428  
   429  				q = obj.Appendp(q, c.newprog)
   430  				q.As = AADD
   431  				q.From.Type = obj.TYPE_CONST
   432  				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   433  				q.Reg = REGSP
   434  				q.To.Type = obj.TYPE_REG
   435  				q.To.Reg = REG_R5
   436  
   437  				q = obj.Appendp(q, c.newprog)
   438  				q.As = ACMP
   439  				q.From.Type = obj.TYPE_REG
   440  				q.From.Reg = REG_R4
   441  				q.To.Type = obj.TYPE_REG
   442  				q.To.Reg = REG_R5
   443  
   444  				q = obj.Appendp(q, c.newprog)
   445  				q.As = ABNE
   446  				q.To.Type = obj.TYPE_BRANCH
   447  				p2 := q
   448  
   449  				q = obj.Appendp(q, c.newprog)
   450  				q.As = AADD
   451  				q.From.Type = obj.TYPE_CONST
   452  				q.From.Offset = c.ctxt.FixedFrameSize()
   453  				q.Reg = REGSP
   454  				q.To.Type = obj.TYPE_REG
   455  				q.To.Reg = REG_R6
   456  
   457  				q = obj.Appendp(q, c.newprog)
   458  				q.As = AMOVD
   459  				q.From.Type = obj.TYPE_REG
   460  				q.From.Reg = REG_R6
   461  				q.To.Type = obj.TYPE_MEM
   462  				q.To.Reg = REG_R3
   463  				q.To.Offset = 0 // Panic.argp
   464  
   465  				q = obj.Appendp(q, c.newprog)
   466  
   467  				q.As = obj.ANOP
   468  				p1.Pcond = q
   469  				p2.Pcond = q
   470  			}
   471  
   472  		case obj.ARET:
   473  			retTarget := p.To.Sym
   474  
   475  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   476  				if autosize == 0 {
   477  					p.As = ABR
   478  					p.From = obj.Addr{}
   479  					if retTarget == nil {
   480  						p.To.Type = obj.TYPE_REG
   481  						p.To.Reg = REG_LR
   482  					} else {
   483  						p.To.Type = obj.TYPE_BRANCH
   484  						p.To.Sym = retTarget
   485  					}
   486  					p.Mark |= BRANCH
   487  					break
   488  				}
   489  
   490  				p.As = AADD
   491  				p.From.Type = obj.TYPE_CONST
   492  				p.From.Offset = int64(autosize)
   493  				p.To.Type = obj.TYPE_REG
   494  				p.To.Reg = REGSP
   495  				p.Spadj = -autosize
   496  
   497  				q = obj.Appendp(p, c.newprog)
   498  				q.As = ABR
   499  				q.From = obj.Addr{}
   500  				q.To.Type = obj.TYPE_REG
   501  				q.To.Reg = REG_LR
   502  				q.Mark |= BRANCH
   503  				q.Spadj = autosize
   504  				break
   505  			}
   506  
   507  			p.As = AMOVD
   508  			p.From.Type = obj.TYPE_MEM
   509  			p.From.Reg = REGSP
   510  			p.From.Offset = 0
   511  			p.To.Type = obj.TYPE_REG
   512  			p.To.Reg = REG_LR
   513  
   514  			q = p
   515  
   516  			if autosize != 0 {
   517  				q = obj.Appendp(q, c.newprog)
   518  				q.As = AADD
   519  				q.From.Type = obj.TYPE_CONST
   520  				q.From.Offset = int64(autosize)
   521  				q.To.Type = obj.TYPE_REG
   522  				q.To.Reg = REGSP
   523  				q.Spadj = -autosize
   524  			}
   525  
   526  			q = obj.Appendp(q, c.newprog)
   527  			q.As = ABR
   528  			q.From = obj.Addr{}
   529  			if retTarget == nil {
   530  				q.To.Type = obj.TYPE_REG
   531  				q.To.Reg = REG_LR
   532  			} else {
   533  				q.To.Type = obj.TYPE_BRANCH
   534  				q.To.Sym = retTarget
   535  			}
   536  			q.Mark |= BRANCH
   537  			q.Spadj = autosize
   538  
   539  		case AADD:
   540  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   541  				p.Spadj = int32(-p.From.Offset)
   542  			}
   543  
   544  		case obj.AGETCALLERPC:
   545  			if cursym.Leaf() {
   546  				/* MOVD LR, Rd */
   547  				p.As = AMOVD
   548  				p.From.Type = obj.TYPE_REG
   549  				p.From.Reg = REG_LR
   550  			} else {
   551  				/* MOVD (RSP), Rd */
   552  				p.As = AMOVD
   553  				p.From.Type = obj.TYPE_MEM
   554  				p.From.Reg = REGSP
   555  			}
   556  		}
   557  	}
   558  	if wasSplit {
   559  		c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
   560  	}
   561  }
   562  
   563  func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   564  	var q *obj.Prog
   565  
   566  	// MOVD	g_stackguard(g), R3
   567  	p = obj.Appendp(p, c.newprog)
   568  
   569  	p.As = AMOVD
   570  	p.From.Type = obj.TYPE_MEM
   571  	p.From.Reg = REGG
   572  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   573  	if c.cursym.CFunc() {
   574  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   575  	}
   576  	p.To.Type = obj.TYPE_REG
   577  	p.To.Reg = REG_R3
   578  
   579  	// Mark the stack bound check and morestack call async nonpreemptible.
   580  	// If we get preempted here, when resumed the preemption request is
   581  	// cleared, but we'll still call morestack, which will double the stack
   582  	// unnecessarily. See issue #35470.
   583  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   584  
   585  	q = nil
   586  	if framesize <= objabi.StackSmall {
   587  		// small stack: SP < stackguard
   588  		//	CMPUBGE	stackguard, SP, label-of-call-to-morestack
   589  
   590  		p = obj.Appendp(p, c.newprog)
   591  		//q1 = p
   592  		p.From.Type = obj.TYPE_REG
   593  		p.From.Reg = REG_R3
   594  		p.Reg = REGSP
   595  		p.As = ACMPUBGE
   596  		p.To.Type = obj.TYPE_BRANCH
   597  
   598  	} else if framesize <= objabi.StackBig {
   599  		// large stack: SP-framesize < stackguard-StackSmall
   600  		//	ADD $-(framesize-StackSmall), SP, R4
   601  		//	CMPUBGE stackguard, R4, label-of-call-to-morestack
   602  		p = obj.Appendp(p, c.newprog)
   603  
   604  		p.As = AADD
   605  		p.From.Type = obj.TYPE_CONST
   606  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   607  		p.Reg = REGSP
   608  		p.To.Type = obj.TYPE_REG
   609  		p.To.Reg = REG_R4
   610  
   611  		p = obj.Appendp(p, c.newprog)
   612  		p.From.Type = obj.TYPE_REG
   613  		p.From.Reg = REG_R3
   614  		p.Reg = REG_R4
   615  		p.As = ACMPUBGE
   616  		p.To.Type = obj.TYPE_BRANCH
   617  
   618  	} else {
   619  		// Such a large stack we need to protect against wraparound.
   620  		// If SP is close to zero:
   621  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   622  		// The +StackGuard on both sides is required to keep the left side positive:
   623  		// SP is allowed to be slightly below stackguard. See stack.h.
   624  		//
   625  		// Preemption sets stackguard to StackPreempt, a very large value.
   626  		// That breaks the math above, so we have to check for that explicitly.
   627  		//	// stackguard is R3
   628  		//	CMP	R3, $StackPreempt
   629  		//	BEQ	label-of-call-to-morestack
   630  		//	ADD	$StackGuard, SP, R4
   631  		//	SUB	R3, R4
   632  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   633  		//	CMPUBGE	TEMP, R4, label-of-call-to-morestack
   634  		p = obj.Appendp(p, c.newprog)
   635  
   636  		p.As = ACMP
   637  		p.From.Type = obj.TYPE_REG
   638  		p.From.Reg = REG_R3
   639  		p.To.Type = obj.TYPE_CONST
   640  		p.To.Offset = objabi.StackPreempt
   641  
   642  		p = obj.Appendp(p, c.newprog)
   643  		q = p
   644  		p.As = ABEQ
   645  		p.To.Type = obj.TYPE_BRANCH
   646  
   647  		p = obj.Appendp(p, c.newprog)
   648  		p.As = AADD
   649  		p.From.Type = obj.TYPE_CONST
   650  		p.From.Offset = int64(objabi.StackGuard)
   651  		p.Reg = REGSP
   652  		p.To.Type = obj.TYPE_REG
   653  		p.To.Reg = REG_R4
   654  
   655  		p = obj.Appendp(p, c.newprog)
   656  		p.As = ASUB
   657  		p.From.Type = obj.TYPE_REG
   658  		p.From.Reg = REG_R3
   659  		p.To.Type = obj.TYPE_REG
   660  		p.To.Reg = REG_R4
   661  
   662  		p = obj.Appendp(p, c.newprog)
   663  		p.As = AMOVD
   664  		p.From.Type = obj.TYPE_CONST
   665  		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
   666  		p.To.Type = obj.TYPE_REG
   667  		p.To.Reg = REGTMP
   668  
   669  		p = obj.Appendp(p, c.newprog)
   670  		p.From.Type = obj.TYPE_REG
   671  		p.From.Reg = REGTMP
   672  		p.Reg = REG_R4
   673  		p.As = ACMPUBGE
   674  		p.To.Type = obj.TYPE_BRANCH
   675  	}
   676  
   677  	return p, q
   678  }
   679  
   680  func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
   681  	// Now we are at the end of the function, but logically
   682  	// we are still in function prologue. We need to fix the
   683  	// SP data and PCDATA.
   684  	spfix := obj.Appendp(p, c.newprog)
   685  	spfix.As = obj.ANOP
   686  	spfix.Spadj = -framesize
   687  
   688  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
   689  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
   690  
   691  	// MOVD	LR, R5
   692  	p = obj.Appendp(pcdata, c.newprog)
   693  	pPre.Pcond = p
   694  	p.As = AMOVD
   695  	p.From.Type = obj.TYPE_REG
   696  	p.From.Reg = REG_LR
   697  	p.To.Type = obj.TYPE_REG
   698  	p.To.Reg = REG_R5
   699  	if pPreempt != nil {
   700  		pPreempt.Pcond = p
   701  	}
   702  
   703  	// BL	runtime.morestack(SB)
   704  	p = obj.Appendp(p, c.newprog)
   705  
   706  	p.As = ABL
   707  	p.To.Type = obj.TYPE_BRANCH
   708  	if c.cursym.CFunc() {
   709  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   710  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
   711  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   712  	} else {
   713  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   714  	}
   715  
   716  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
   717  
   718  	// BR	start
   719  	p = obj.Appendp(p, c.newprog)
   720  
   721  	p.As = ABR
   722  	p.To.Type = obj.TYPE_BRANCH
   723  	p.Pcond = c.cursym.Func.Text.Link
   724  	return p
   725  }
   726  
   727  var unaryDst = map[obj.As]bool{
   728  	ASTCK:  true,
   729  	ASTCKC: true,
   730  	ASTCKE: true,
   731  	ASTCKF: true,
   732  	ANEG:   true,
   733  	ANEGW:  true,
   734  	AVONE:  true,
   735  	AVZERO: true,
   736  }
   737  
   738  var Links390x = obj.LinkArch{
   739  	Arch:           sys.ArchS390X,
   740  	Init:           buildop,
   741  	Preprocess:     preprocess,
   742  	Assemble:       spanz,
   743  	Progedit:       progedit,
   744  	UnaryDst:       unaryDst,
   745  	DWARFRegisters: S390XDWARFRegisters,
   746  }