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

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

View as plain text