Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/internal/obj/s390x/objz.go

Documentation: cmd/internal/obj/s390x

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

View as plain text