github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/s390x/objz.go (about)

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