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