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