github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/src/cmd/internal/obj/s390x/objz.go (about)

     1  // Based on cmd/internal/obj/ppc64/obj9.go.
     2  //
     3  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     4  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     5  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     6  //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
     7  //	Portions Copyright © 2004,2006 Bruce Ellis
     8  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
     9  //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
    10  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    11  //
    12  // Permission is hereby granted, free of charge, to any person obtaining a copy
    13  // of this software and associated documentation files (the "Software"), to deal
    14  // in the Software without restriction, including without limitation the rights
    15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    16  // copies of the Software, and to permit persons to whom the Software is
    17  // furnished to do so, subject to the following conditions:
    18  //
    19  // The above copyright notice and this permission notice shall be included in
    20  // all copies or substantial portions of the Software.
    21  //
    22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    28  // THE SOFTWARE.
    29  
    30  package s390x
    31  
    32  import (
    33  	"cmd/internal/obj"
    34  	"cmd/internal/objabi"
    35  	"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  			ABEQ,
   253  			ABGE,
   254  			ABGT,
   255  			ABLE,
   256  			ABLT,
   257  			ABLEU,
   258  			ABLTU,
   259  			ABNE,
   260  			ABR,
   261  			ABVC,
   262  			ABVS,
   263  			ACMPBEQ,
   264  			ACMPBGE,
   265  			ACMPBGT,
   266  			ACMPBLE,
   267  			ACMPBLT,
   268  			ACMPBNE,
   269  			ACMPUBEQ,
   270  			ACMPUBGE,
   271  			ACMPUBGT,
   272  			ACMPUBLE,
   273  			ACMPUBLT,
   274  			ACMPUBNE:
   275  			q = p
   276  			p.Mark |= BRANCH
   277  			if p.Pcond != nil {
   278  				q := p.Pcond
   279  				for q.As == obj.ANOP {
   280  					q = q.Link
   281  					p.Pcond = q
   282  				}
   283  			}
   284  
   285  		case obj.ANOP:
   286  			q.Link = p.Link /* q is non-nop */
   287  			p.Link.Mark |= p.Mark
   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  	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.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 = c.stacksplitPre(p, autosize) // emit pre part of split check
   328  				pPre = p
   329  				wasSplit = true //need post part of split
   330  			}
   331  
   332  			if autosize != 0 {
   333  				// Make sure to save link register for non-empty frame, even if
   334  				// it is a leaf function, so that traceback works.
   335  				// Store link register before decrementing SP, so if a signal comes
   336  				// during the execution of the function prologue, the traceback
   337  				// code will not see a half-updated stack frame.
   338  				q = obj.Appendp(p, c.newprog)
   339  				q.As = AMOVD
   340  				q.From.Type = obj.TYPE_REG
   341  				q.From.Reg = REG_LR
   342  				q.To.Type = obj.TYPE_MEM
   343  				q.To.Reg = REGSP
   344  				q.To.Offset = int64(-autosize)
   345  
   346  				q = obj.Appendp(q, c.newprog)
   347  				q.As = AMOVD
   348  				q.From.Type = obj.TYPE_ADDR
   349  				q.From.Offset = int64(-autosize)
   350  				q.From.Reg = REGSP // not actually needed - REGSP is assumed if no reg is provided
   351  				q.To.Type = obj.TYPE_REG
   352  				q.To.Reg = REGSP
   353  				q.Spadj = autosize
   354  			} else if c.cursym.Func.Text.Mark&LEAF == 0 {
   355  				// A very few functions that do not return to their caller
   356  				// (e.g. gogo) are not identified as leaves but still have
   357  				// no frame.
   358  				c.cursym.Func.Text.Mark |= LEAF
   359  			}
   360  
   361  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   362  				c.cursym.Set(obj.AttrLeaf, true)
   363  				break
   364  			}
   365  
   366  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   367  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   368  				//
   369  				//	MOVD g_panic(g), R3
   370  				//	CMP R3, $0
   371  				//	BEQ end
   372  				//	MOVD panic_argp(R3), R4
   373  				//	ADD $(autosize+8), R1, R5
   374  				//	CMP R4, R5
   375  				//	BNE end
   376  				//	ADD $8, R1, R6
   377  				//	MOVD R6, panic_argp(R3)
   378  				// end:
   379  				//	NOP
   380  				//
   381  				// The NOP is needed to give the jumps somewhere to land.
   382  				// It is a liblink NOP, not a s390x NOP: it encodes to 0 instruction bytes.
   383  
   384  				q = obj.Appendp(q, c.newprog)
   385  
   386  				q.As = AMOVD
   387  				q.From.Type = obj.TYPE_MEM
   388  				q.From.Reg = REGG
   389  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   390  				q.To.Type = obj.TYPE_REG
   391  				q.To.Reg = REG_R3
   392  
   393  				q = obj.Appendp(q, c.newprog)
   394  				q.As = ACMP
   395  				q.From.Type = obj.TYPE_REG
   396  				q.From.Reg = REG_R3
   397  				q.To.Type = obj.TYPE_CONST
   398  				q.To.Offset = 0
   399  
   400  				q = obj.Appendp(q, c.newprog)
   401  				q.As = ABEQ
   402  				q.To.Type = obj.TYPE_BRANCH
   403  				p1 := q
   404  
   405  				q = obj.Appendp(q, c.newprog)
   406  				q.As = AMOVD
   407  				q.From.Type = obj.TYPE_MEM
   408  				q.From.Reg = REG_R3
   409  				q.From.Offset = 0 // Panic.argp
   410  				q.To.Type = obj.TYPE_REG
   411  				q.To.Reg = REG_R4
   412  
   413  				q = obj.Appendp(q, c.newprog)
   414  				q.As = AADD
   415  				q.From.Type = obj.TYPE_CONST
   416  				q.From.Offset = int64(autosize) + c.ctxt.FixedFrameSize()
   417  				q.Reg = REGSP
   418  				q.To.Type = obj.TYPE_REG
   419  				q.To.Reg = REG_R5
   420  
   421  				q = obj.Appendp(q, c.newprog)
   422  				q.As = ACMP
   423  				q.From.Type = obj.TYPE_REG
   424  				q.From.Reg = REG_R4
   425  				q.To.Type = obj.TYPE_REG
   426  				q.To.Reg = REG_R5
   427  
   428  				q = obj.Appendp(q, c.newprog)
   429  				q.As = ABNE
   430  				q.To.Type = obj.TYPE_BRANCH
   431  				p2 := q
   432  
   433  				q = obj.Appendp(q, c.newprog)
   434  				q.As = AADD
   435  				q.From.Type = obj.TYPE_CONST
   436  				q.From.Offset = c.ctxt.FixedFrameSize()
   437  				q.Reg = REGSP
   438  				q.To.Type = obj.TYPE_REG
   439  				q.To.Reg = REG_R6
   440  
   441  				q = obj.Appendp(q, c.newprog)
   442  				q.As = AMOVD
   443  				q.From.Type = obj.TYPE_REG
   444  				q.From.Reg = REG_R6
   445  				q.To.Type = obj.TYPE_MEM
   446  				q.To.Reg = REG_R3
   447  				q.To.Offset = 0 // Panic.argp
   448  
   449  				q = obj.Appendp(q, c.newprog)
   450  
   451  				q.As = obj.ANOP
   452  				p1.Pcond = q
   453  				p2.Pcond = q
   454  			}
   455  
   456  		case obj.ARET:
   457  			retTarget := p.To.Sym
   458  
   459  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   460  				if autosize == 0 {
   461  					p.As = ABR
   462  					p.From = obj.Addr{}
   463  					if retTarget == nil {
   464  						p.To.Type = obj.TYPE_REG
   465  						p.To.Reg = REG_LR
   466  					} else {
   467  						p.To.Type = obj.TYPE_BRANCH
   468  						p.To.Sym = retTarget
   469  					}
   470  					p.Mark |= BRANCH
   471  					break
   472  				}
   473  
   474  				p.As = AADD
   475  				p.From.Type = obj.TYPE_CONST
   476  				p.From.Offset = int64(autosize)
   477  				p.To.Type = obj.TYPE_REG
   478  				p.To.Reg = REGSP
   479  				p.Spadj = -autosize
   480  
   481  				q = obj.Appendp(p, c.newprog)
   482  				q.As = ABR
   483  				q.From = obj.Addr{}
   484  				q.To.Type = obj.TYPE_REG
   485  				q.To.Reg = REG_LR
   486  				q.Mark |= BRANCH
   487  				q.Spadj = autosize
   488  				break
   489  			}
   490  
   491  			p.As = AMOVD
   492  			p.From.Type = obj.TYPE_MEM
   493  			p.From.Reg = REGSP
   494  			p.From.Offset = 0
   495  			p.To.Type = obj.TYPE_REG
   496  			p.To.Reg = REG_LR
   497  
   498  			q = p
   499  
   500  			if autosize != 0 {
   501  				q = obj.Appendp(q, c.newprog)
   502  				q.As = AADD
   503  				q.From.Type = obj.TYPE_CONST
   504  				q.From.Offset = int64(autosize)
   505  				q.To.Type = obj.TYPE_REG
   506  				q.To.Reg = REGSP
   507  				q.Spadj = -autosize
   508  			}
   509  
   510  			q = obj.Appendp(q, c.newprog)
   511  			q.As = ABR
   512  			q.From = obj.Addr{}
   513  			if retTarget == nil {
   514  				q.To.Type = obj.TYPE_REG
   515  				q.To.Reg = REG_LR
   516  			} else {
   517  				q.To.Type = obj.TYPE_BRANCH
   518  				q.To.Sym = retTarget
   519  			}
   520  			q.Mark |= BRANCH
   521  			q.Spadj = autosize
   522  
   523  		case AADD:
   524  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   525  				p.Spadj = int32(-p.From.Offset)
   526  			}
   527  		}
   528  	}
   529  	if wasSplit {
   530  		c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
   531  	}
   532  }
   533  
   534  func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
   535  	var q *obj.Prog
   536  
   537  	// MOVD	g_stackguard(g), R3
   538  	p = obj.Appendp(p, c.newprog)
   539  
   540  	p.As = AMOVD
   541  	p.From.Type = obj.TYPE_MEM
   542  	p.From.Reg = REGG
   543  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   544  	if c.cursym.CFunc() {
   545  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   546  	}
   547  	p.To.Type = obj.TYPE_REG
   548  	p.To.Reg = REG_R3
   549  
   550  	q = nil
   551  	if framesize <= objabi.StackSmall {
   552  		// small stack: SP < stackguard
   553  		//	CMP	stackguard, SP
   554  
   555  		//p.To.Type = obj.TYPE_REG
   556  		//p.To.Reg = REGSP
   557  
   558  		// q1: BLT	done
   559  
   560  		p = obj.Appendp(p, c.newprog)
   561  		//q1 = p
   562  		p.From.Type = obj.TYPE_REG
   563  		p.From.Reg = REG_R3
   564  		p.Reg = REGSP
   565  		p.As = ACMPUBGE
   566  		p.To.Type = obj.TYPE_BRANCH
   567  		//p = obj.Appendp(ctxt, p)
   568  
   569  		//p.As = ACMPU
   570  		//p.From.Type = obj.TYPE_REG
   571  		//p.From.Reg = REG_R3
   572  		//p.To.Type = obj.TYPE_REG
   573  		//p.To.Reg = REGSP
   574  
   575  		//p = obj.Appendp(ctxt, p)
   576  		//p.As = ABGE
   577  		//p.To.Type = obj.TYPE_BRANCH
   578  
   579  	} else if framesize <= objabi.StackBig {
   580  		// large stack: SP-framesize < stackguard-StackSmall
   581  		//	ADD $-(framesize-StackSmall), SP, R4
   582  		//	CMP stackguard, R4
   583  		p = obj.Appendp(p, c.newprog)
   584  
   585  		p.As = AADD
   586  		p.From.Type = obj.TYPE_CONST
   587  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   588  		p.Reg = REGSP
   589  		p.To.Type = obj.TYPE_REG
   590  		p.To.Reg = REG_R4
   591  
   592  		p = obj.Appendp(p, c.newprog)
   593  		p.From.Type = obj.TYPE_REG
   594  		p.From.Reg = REG_R3
   595  		p.Reg = REG_R4
   596  		p.As = ACMPUBGE
   597  		p.To.Type = obj.TYPE_BRANCH
   598  
   599  	} else {
   600  		// Such a large stack we need to protect against wraparound.
   601  		// If SP is close to zero:
   602  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   603  		// The +StackGuard on both sides is required to keep the left side positive:
   604  		// SP is allowed to be slightly below stackguard. See stack.h.
   605  		//
   606  		// Preemption sets stackguard to StackPreempt, a very large value.
   607  		// That breaks the math above, so we have to check for that explicitly.
   608  		//	// stackguard is R3
   609  		//	CMP	R3, $StackPreempt
   610  		//	BEQ	label-of-call-to-morestack
   611  		//	ADD	$StackGuard, SP, R4
   612  		//	SUB	R3, R4
   613  		//	MOVD	$(framesize+(StackGuard-StackSmall)), TEMP
   614  		//	CMPUBGE	TEMP, R4
   615  		p = obj.Appendp(p, c.newprog)
   616  
   617  		p.As = ACMP
   618  		p.From.Type = obj.TYPE_REG
   619  		p.From.Reg = REG_R3
   620  		p.To.Type = obj.TYPE_CONST
   621  		p.To.Offset = objabi.StackPreempt
   622  
   623  		p = obj.Appendp(p, c.newprog)
   624  		q = p
   625  		p.As = ABEQ
   626  		p.To.Type = obj.TYPE_BRANCH
   627  
   628  		p = obj.Appendp(p, c.newprog)
   629  		p.As = AADD
   630  		p.From.Type = obj.TYPE_CONST
   631  		p.From.Offset = objabi.StackGuard
   632  		p.Reg = REGSP
   633  		p.To.Type = obj.TYPE_REG
   634  		p.To.Reg = REG_R4
   635  
   636  		p = obj.Appendp(p, c.newprog)
   637  		p.As = ASUB
   638  		p.From.Type = obj.TYPE_REG
   639  		p.From.Reg = REG_R3
   640  		p.To.Type = obj.TYPE_REG
   641  		p.To.Reg = REG_R4
   642  
   643  		p = obj.Appendp(p, c.newprog)
   644  		p.As = AMOVD
   645  		p.From.Type = obj.TYPE_CONST
   646  		p.From.Offset = int64(framesize) + objabi.StackGuard - objabi.StackSmall
   647  		p.To.Type = obj.TYPE_REG
   648  		p.To.Reg = REGTMP
   649  
   650  		p = obj.Appendp(p, c.newprog)
   651  		p.From.Type = obj.TYPE_REG
   652  		p.From.Reg = REGTMP
   653  		p.Reg = REG_R4
   654  		p.As = ACMPUBGE
   655  		p.To.Type = obj.TYPE_BRANCH
   656  	}
   657  
   658  	return p, q
   659  }
   660  
   661  func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
   662  	// Now we are at the end of the function, but logically
   663  	// we are still in function prologue. We need to fix the
   664  	// SP data and PCDATA.
   665  	spfix := obj.Appendp(p, c.newprog)
   666  	spfix.As = obj.ANOP
   667  	spfix.Spadj = -framesize
   668  
   669  	pcdata := obj.Appendp(spfix, c.newprog)
   670  	pcdata.Pos = c.cursym.Func.Text.Pos
   671  	pcdata.As = obj.APCDATA
   672  	pcdata.From.Type = obj.TYPE_CONST
   673  	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
   674  	pcdata.To.Type = obj.TYPE_CONST
   675  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   676  
   677  	// MOVD	LR, R5
   678  	p = obj.Appendp(pcdata, c.newprog)
   679  	pPre.Pcond = p
   680  	p.As = AMOVD
   681  	p.From.Type = obj.TYPE_REG
   682  	p.From.Reg = REG_LR
   683  	p.To.Type = obj.TYPE_REG
   684  	p.To.Reg = REG_R5
   685  	if pPreempt != nil {
   686  		pPreempt.Pcond = p
   687  	}
   688  
   689  	// BL	runtime.morestack(SB)
   690  	p = obj.Appendp(p, c.newprog)
   691  
   692  	p.As = ABL
   693  	p.To.Type = obj.TYPE_BRANCH
   694  	if c.cursym.CFunc() {
   695  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   696  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
   697  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   698  	} else {
   699  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   700  	}
   701  
   702  	// BR	start
   703  	p = obj.Appendp(p, c.newprog)
   704  
   705  	p.As = ABR
   706  	p.To.Type = obj.TYPE_BRANCH
   707  	p.Pcond = c.cursym.Func.Text.Link
   708  	return p
   709  }
   710  
   711  var unaryDst = map[obj.As]bool{
   712  	ASTCK:  true,
   713  	ASTCKC: true,
   714  	ASTCKE: true,
   715  	ASTCKF: true,
   716  	ANEG:   true,
   717  	ANEGW:  true,
   718  	AVONE:  true,
   719  	AVZERO: true,
   720  }
   721  
   722  var Links390x = obj.LinkArch{
   723  	Arch:       sys.ArchS390X,
   724  	Init:       buildop,
   725  	Preprocess: preprocess,
   726  	Assemble:   spanz,
   727  	Progedit:   progedit,
   728  	UnaryDst:   unaryDst,
   729  }