Source file src/cmd/internal/obj/arm/obj5.go

     1  // Derived from Inferno utils/5c/swt.c
     2  // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/swt.c
     3  //
     4  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
     5  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  //	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  //	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  //	Portions Copyright © 2004,2006 Bruce Ellis
     9  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  //	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  package arm
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"cmd/internal/objabi"
    36  	"cmd/internal/sys"
    37  	"internal/abi"
    38  	"internal/buildcfg"
    39  	"log"
    40  )
    41  
    42  var progedit_tlsfallback *obj.LSym
    43  
    44  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    45  	p.From.Class = 0
    46  	p.To.Class = 0
    47  
    48  	c := ctxt5{ctxt: ctxt, newprog: newprog}
    49  
    50  	// Rewrite B/BL to symbol as TYPE_BRANCH.
    51  	switch p.As {
    52  	case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
    53  		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
    54  			p.To.Type = obj.TYPE_BRANCH
    55  		}
    56  	}
    57  
    58  	// Replace TLS register fetches on older ARM processors.
    59  	switch p.As {
    60  	// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
    61  	case AMRC:
    62  		if p.To.Offset&0xffff0fff == 0xee1d0f70 {
    63  			// Because the instruction might be rewritten to a BL which returns in R0
    64  			// the register must be zero.
    65  			if p.To.Offset&0xf000 != 0 {
    66  				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
    67  			}
    68  
    69  			if buildcfg.GOARM.Version < 7 {
    70  				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
    71  				if progedit_tlsfallback == nil {
    72  					progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
    73  				}
    74  
    75  				// MOVW	LR, R11
    76  				p.As = AMOVW
    77  
    78  				p.From.Type = obj.TYPE_REG
    79  				p.From.Reg = REGLINK
    80  				p.To.Type = obj.TYPE_REG
    81  				p.To.Reg = REGTMP
    82  
    83  				// BL	runtime.read_tls_fallback(SB)
    84  				p = obj.Appendp(p, newprog)
    85  
    86  				p.As = ABL
    87  				p.To.Type = obj.TYPE_BRANCH
    88  				p.To.Sym = progedit_tlsfallback
    89  				p.To.Offset = 0
    90  
    91  				// MOVW	R11, LR
    92  				p = obj.Appendp(p, newprog)
    93  
    94  				p.As = AMOVW
    95  				p.From.Type = obj.TYPE_REG
    96  				p.From.Reg = REGTMP
    97  				p.To.Type = obj.TYPE_REG
    98  				p.To.Reg = REGLINK
    99  				break
   100  			}
   101  		}
   102  
   103  		// Otherwise, MRC/MCR instructions need no further treatment.
   104  		p.As = AWORD
   105  	}
   106  
   107  	// Rewrite float constants to values stored in memory.
   108  	switch p.As {
   109  	case AMOVF:
   110  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   111  			f32 := float32(p.From.Val.(float64))
   112  			p.From.Type = obj.TYPE_MEM
   113  			p.From.Sym = ctxt.Float32Sym(f32)
   114  			p.From.Name = obj.NAME_EXTERN
   115  			p.From.Offset = 0
   116  		}
   117  
   118  	case AMOVD:
   119  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   120  			p.From.Type = obj.TYPE_MEM
   121  			p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
   122  			p.From.Name = obj.NAME_EXTERN
   123  			p.From.Offset = 0
   124  		}
   125  	}
   126  
   127  	if ctxt.Flag_dynlink {
   128  		c.rewriteToUseGot(p)
   129  	}
   130  }
   131  
   132  // Rewrite p, if necessary, to access global data via the global offset table.
   133  func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
   134  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   135  		//     ADUFFxxx $offset
   136  		// becomes
   137  		//     MOVW runtime.duffxxx@GOT, R9
   138  		//     ADD $offset, R9
   139  		//     CALL (R9)
   140  		var sym *obj.LSym
   141  		if p.As == obj.ADUFFZERO {
   142  			sym = c.ctxt.Lookup("runtime.duffzero")
   143  		} else {
   144  			sym = c.ctxt.Lookup("runtime.duffcopy")
   145  		}
   146  		offset := p.To.Offset
   147  		p.As = AMOVW
   148  		p.From.Type = obj.TYPE_MEM
   149  		p.From.Name = obj.NAME_GOTREF
   150  		p.From.Sym = sym
   151  		p.To.Type = obj.TYPE_REG
   152  		p.To.Reg = REG_R9
   153  		p.To.Name = obj.NAME_NONE
   154  		p.To.Offset = 0
   155  		p.To.Sym = nil
   156  		p1 := obj.Appendp(p, c.newprog)
   157  		p1.As = AADD
   158  		p1.From.Type = obj.TYPE_CONST
   159  		p1.From.Offset = offset
   160  		p1.To.Type = obj.TYPE_REG
   161  		p1.To.Reg = REG_R9
   162  		p2 := obj.Appendp(p1, c.newprog)
   163  		p2.As = obj.ACALL
   164  		p2.To.Type = obj.TYPE_MEM
   165  		p2.To.Reg = REG_R9
   166  		return
   167  	}
   168  
   169  	// We only care about global data: NAME_EXTERN means a global
   170  	// symbol in the Go sense, and p.Sym.Local is true for a few
   171  	// internally defined symbols.
   172  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   173  		// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
   174  		// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
   175  		if p.As != AMOVW {
   176  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   177  		}
   178  		if p.To.Type != obj.TYPE_REG {
   179  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   180  		}
   181  		p.From.Type = obj.TYPE_MEM
   182  		p.From.Name = obj.NAME_GOTREF
   183  		if p.From.Offset != 0 {
   184  			q := obj.Appendp(p, c.newprog)
   185  			q.As = AADD
   186  			q.From.Type = obj.TYPE_CONST
   187  			q.From.Offset = p.From.Offset
   188  			q.To = p.To
   189  			p.From.Offset = 0
   190  		}
   191  	}
   192  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   193  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   194  	}
   195  	var source *obj.Addr
   196  	// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
   197  	// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
   198  	// An addition may be inserted between the two MOVs if there is an offset.
   199  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   200  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   201  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   202  		}
   203  		source = &p.From
   204  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   205  		source = &p.To
   206  	} else {
   207  		return
   208  	}
   209  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   210  		return
   211  	}
   212  	if source.Sym.Type == objabi.STLSBSS {
   213  		return
   214  	}
   215  	if source.Type != obj.TYPE_MEM {
   216  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   217  	}
   218  	p1 := obj.Appendp(p, c.newprog)
   219  	p2 := obj.Appendp(p1, c.newprog)
   220  
   221  	p1.As = AMOVW
   222  	p1.From.Type = obj.TYPE_MEM
   223  	p1.From.Sym = source.Sym
   224  	p1.From.Name = obj.NAME_GOTREF
   225  	p1.To.Type = obj.TYPE_REG
   226  	p1.To.Reg = REG_R9
   227  
   228  	p2.As = p.As
   229  	p2.From = p.From
   230  	p2.To = p.To
   231  	if p.From.Name == obj.NAME_EXTERN {
   232  		p2.From.Reg = REG_R9
   233  		p2.From.Name = obj.NAME_NONE
   234  		p2.From.Sym = nil
   235  	} else if p.To.Name == obj.NAME_EXTERN {
   236  		p2.To.Reg = REG_R9
   237  		p2.To.Name = obj.NAME_NONE
   238  		p2.To.Sym = nil
   239  	} else {
   240  		return
   241  	}
   242  	obj.Nopout(p)
   243  }
   244  
   245  // Prog.mark
   246  const (
   247  	FOLL  = 1 << 0
   248  	LABEL = 1 << 1
   249  	LEAF  = 1 << 2
   250  )
   251  
   252  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   253  	autosize := int32(0)
   254  
   255  	if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
   256  		return
   257  	}
   258  
   259  	c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
   260  
   261  	p := c.cursym.Func().Text
   262  	autoffset := int32(p.To.Offset)
   263  	if autoffset == -4 {
   264  		// Historical way to mark NOFRAME.
   265  		p.From.Sym.Set(obj.AttrNoFrame, true)
   266  		autoffset = 0
   267  	}
   268  	if autoffset < 0 || autoffset%4 != 0 {
   269  		c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
   270  	}
   271  	if p.From.Sym.NoFrame() {
   272  		if autoffset != 0 {
   273  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
   274  		}
   275  	}
   276  
   277  	cursym.Func().Locals = autoffset
   278  	cursym.Func().Args = p.To.Val.(int32)
   279  
   280  	/*
   281  	 * find leaf subroutines
   282  	 */
   283  	for p := cursym.Func().Text; p != nil; p = p.Link {
   284  		switch p.As {
   285  		case obj.ATEXT:
   286  			p.Mark |= LEAF
   287  
   288  		case ADIV, ADIVU, AMOD, AMODU:
   289  			cursym.Func().Text.Mark &^= LEAF
   290  
   291  		case ABL,
   292  			ABX,
   293  			obj.ADUFFZERO,
   294  			obj.ADUFFCOPY:
   295  			cursym.Func().Text.Mark &^= LEAF
   296  		}
   297  	}
   298  
   299  	var q2 *obj.Prog
   300  	for p := cursym.Func().Text; p != nil; p = p.Link {
   301  		o := p.As
   302  		switch o {
   303  		case obj.ATEXT:
   304  			autosize = autoffset
   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 += 4
   315  			}
   316  
   317  			if autosize == 0 && cursym.Func().Text.Mark&LEAF == 0 {
   318  				// A very few functions that do not return to their caller
   319  				// are not identified as leaves but still have no frame.
   320  				if ctxt.Debugvlog {
   321  					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
   322  				}
   323  
   324  				cursym.Func().Text.Mark |= LEAF
   325  			}
   326  
   327  			// FP offsets need an updated p.To.Offset.
   328  			p.To.Offset = int64(autosize) - 4
   329  
   330  			if cursym.Func().Text.Mark&LEAF != 0 {
   331  				cursym.Set(obj.AttrLeaf, true)
   332  				if p.From.Sym.NoFrame() {
   333  					break
   334  				}
   335  			}
   336  
   337  			if !p.From.Sym.NoSplit() {
   338  				p = c.stacksplit(p, autosize) // emit split check
   339  			}
   340  
   341  			// MOVW.W		R14,$-autosize(SP)
   342  			p = obj.Appendp(p, c.newprog)
   343  
   344  			p.As = AMOVW
   345  			p.Scond |= C_WBIT
   346  			p.From.Type = obj.TYPE_REG
   347  			p.From.Reg = REGLINK
   348  			p.To.Type = obj.TYPE_MEM
   349  			p.To.Offset = int64(-autosize)
   350  			p.To.Reg = REGSP
   351  			p.Spadj = autosize
   352  
   353  		case obj.ARET:
   354  			nocache(p)
   355  
   356  			retSym, retReg := p.To.Sym, p.To.Reg
   357  			if retReg == obj.REG_NONE {
   358  				retReg = REGLINK
   359  			}
   360  			p.To.Sym = nil
   361  			p.To.Name = obj.NAME_NONE
   362  			p.To.Reg = obj.REG_NONE
   363  
   364  			if cursym.Func().Text.Mark&LEAF != 0 {
   365  				if autosize == 0 {
   366  					p.As = AB
   367  					p.From = obj.Addr{}
   368  					if retSym != nil { // retjmp
   369  						p.To.Type = obj.TYPE_BRANCH
   370  						p.To.Name = obj.NAME_EXTERN
   371  						p.To.Sym = retSym
   372  					} else {
   373  						p.To.Type = obj.TYPE_MEM
   374  						p.To.Offset = 0
   375  						p.To.Reg = retReg
   376  					}
   377  
   378  					break
   379  				}
   380  			}
   381  
   382  			p.As = AMOVW
   383  			p.Scond |= C_PBIT
   384  			p.From.Type = obj.TYPE_MEM
   385  			p.From.Offset = int64(autosize)
   386  			p.From.Reg = REGSP
   387  			p.To.Type = obj.TYPE_REG
   388  			p.To.Reg = REGPC
   389  
   390  			// If there are instructions following
   391  			// this ARET, they come from a branch
   392  			// with the same stackframe, so spadj should
   393  			// sum to 0.
   394  
   395  			if retSym != nil || retReg != REGLINK { // retjmp
   396  				p.To.Reg = REGLINK
   397  				// If ARET is a tail-call, the frame pop
   398  				// and jump are in separate instructions
   399  				// and spadj is needed.
   400  				p.Spadj = -autosize
   401  				q2 = obj.Appendp(p, newprog)
   402  				q2.As = AB
   403  				q2.Spadj = +autosize
   404  				if retSym != nil {
   405  					q2.To.Type = obj.TYPE_BRANCH
   406  					q2.To.Sym = retSym
   407  				} else {
   408  					q2.To.Type = obj.TYPE_MEM
   409  					q2.To.Reg = retReg
   410  				}
   411  				p = q2
   412  			}
   413  
   414  		case AADD:
   415  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   416  				p.Spadj = int32(-p.From.Offset)
   417  			}
   418  
   419  		case ASUB:
   420  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   421  				p.Spadj = int32(p.From.Offset)
   422  			}
   423  
   424  		case ADIV, ADIVU, AMOD, AMODU:
   425  			if cursym.Func().Text.From.Sym.NoSplit() {
   426  				ctxt.Diag("cannot divide in NOSPLIT function")
   427  			}
   428  			const debugdivmod = false
   429  			if debugdivmod {
   430  				break
   431  			}
   432  			if p.From.Type != obj.TYPE_REG {
   433  				break
   434  			}
   435  			if p.To.Type != obj.TYPE_REG {
   436  				break
   437  			}
   438  
   439  			// Make copy because we overwrite p below.
   440  			q1 := *p
   441  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   442  				ctxt.Diag("div already using REGTMP: %v", p)
   443  			}
   444  
   445  			/* MOV m(g),REGTMP */
   446  			p.As = AMOVW
   447  			p.Pos = q1.Pos
   448  			p.From.Type = obj.TYPE_MEM
   449  			p.From.Reg = REGG
   450  			p.From.Offset = 6 * 4 // offset of g.m
   451  			p.Reg = 0
   452  			p.To.Type = obj.TYPE_REG
   453  			p.To.Reg = REGTMP
   454  
   455  			/* MOV a,m_divmod(REGTMP) */
   456  			p = obj.Appendp(p, newprog)
   457  			p.As = AMOVW
   458  			p.Pos = q1.Pos
   459  			p.From.Type = obj.TYPE_REG
   460  			p.From.Reg = q1.From.Reg
   461  			p.To.Type = obj.TYPE_MEM
   462  			p.To.Reg = REGTMP
   463  			p.To.Offset = 7 * 4 // offset of m.divmod
   464  
   465  			/* MOV b, R8 */
   466  			p = obj.Appendp(p, newprog)
   467  			p.As = AMOVW
   468  			p.Pos = q1.Pos
   469  			p.From.Type = obj.TYPE_REG
   470  			p.From.Reg = q1.Reg
   471  			if q1.Reg == 0 {
   472  				p.From.Reg = q1.To.Reg
   473  			}
   474  			p.To.Type = obj.TYPE_REG
   475  			p.To.Reg = REG_R8
   476  			p.To.Offset = 0
   477  
   478  			/* CALL appropriate */
   479  			p = obj.Appendp(p, newprog)
   480  			p.As = ABL
   481  			p.Pos = q1.Pos
   482  			p.To.Type = obj.TYPE_BRANCH
   483  			switch o {
   484  			case ADIV:
   485  				p.To.Sym = symdiv
   486  			case ADIVU:
   487  				p.To.Sym = symdivu
   488  			case AMOD:
   489  				p.To.Sym = symmod
   490  			case AMODU:
   491  				p.To.Sym = symmodu
   492  			}
   493  
   494  			/* MOV REGTMP, b */
   495  			p = obj.Appendp(p, newprog)
   496  			p.As = AMOVW
   497  			p.Pos = q1.Pos
   498  			p.From.Type = obj.TYPE_REG
   499  			p.From.Reg = REGTMP
   500  			p.From.Offset = 0
   501  			p.To.Type = obj.TYPE_REG
   502  			p.To.Reg = q1.To.Reg
   503  
   504  		case AMOVW:
   505  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   506  				p.Spadj = int32(-p.To.Offset)
   507  			}
   508  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   509  				p.Spadj = int32(-p.From.Offset)
   510  			}
   511  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   512  				p.Spadj = int32(-p.From.Offset)
   513  			}
   514  
   515  		case obj.AGETCALLERPC:
   516  			if cursym.Leaf() {
   517  				/* MOVW LR, Rd */
   518  				p.As = AMOVW
   519  				p.From.Type = obj.TYPE_REG
   520  				p.From.Reg = REGLINK
   521  			} else {
   522  				/* MOVW (RSP), Rd */
   523  				p.As = AMOVW
   524  				p.From.Type = obj.TYPE_MEM
   525  				p.From.Reg = REGSP
   526  			}
   527  		}
   528  
   529  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
   530  			f := c.cursym.Func()
   531  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
   532  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
   533  				if ctxt.Debugvlog || !ctxt.IsAsm {
   534  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
   535  					if !ctxt.IsAsm {
   536  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
   537  						ctxt.DiagFlush()
   538  						log.Fatalf("bad SPWRITE")
   539  					}
   540  				}
   541  			}
   542  		}
   543  	}
   544  }
   545  
   546  func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   547  	if c.ctxt.Flag_maymorestack != "" {
   548  		// Save LR and make room for REGCTXT.
   549  		const frameSize = 8
   550  		// MOVW.W R14,$-8(SP)
   551  		p = obj.Appendp(p, c.newprog)
   552  		p.As = AMOVW
   553  		p.Scond |= C_WBIT
   554  		p.From.Type = obj.TYPE_REG
   555  		p.From.Reg = REGLINK
   556  		p.To.Type = obj.TYPE_MEM
   557  		p.To.Offset = -frameSize
   558  		p.To.Reg = REGSP
   559  		p.Spadj = frameSize
   560  
   561  		// MOVW REGCTXT, 4(SP)
   562  		p = obj.Appendp(p, c.newprog)
   563  		p.As = AMOVW
   564  		p.From.Type = obj.TYPE_REG
   565  		p.From.Reg = REGCTXT
   566  		p.To.Type = obj.TYPE_MEM
   567  		p.To.Offset = 4
   568  		p.To.Reg = REGSP
   569  
   570  		// CALL maymorestack
   571  		p = obj.Appendp(p, c.newprog)
   572  		p.As = obj.ACALL
   573  		p.To.Type = obj.TYPE_BRANCH
   574  		// See ../x86/obj6.go
   575  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
   576  
   577  		// Restore REGCTXT and LR.
   578  
   579  		// MOVW 4(SP), REGCTXT
   580  		p = obj.Appendp(p, c.newprog)
   581  		p.As = AMOVW
   582  		p.From.Type = obj.TYPE_MEM
   583  		p.From.Offset = 4
   584  		p.From.Reg = REGSP
   585  		p.To.Type = obj.TYPE_REG
   586  		p.To.Reg = REGCTXT
   587  
   588  		// MOVW.P 8(SP), R14
   589  		p.As = AMOVW
   590  		p.Scond |= C_PBIT
   591  		p.From.Type = obj.TYPE_MEM
   592  		p.From.Offset = frameSize
   593  		p.From.Reg = REGSP
   594  		p.To.Type = obj.TYPE_REG
   595  		p.To.Reg = REGLINK
   596  		p.Spadj = -frameSize
   597  	}
   598  
   599  	// Jump back to here after morestack returns.
   600  	startPred := p
   601  
   602  	// MOVW g_stackguard(g), R1
   603  	p = obj.Appendp(p, c.newprog)
   604  
   605  	p.As = AMOVW
   606  	p.From.Type = obj.TYPE_MEM
   607  	p.From.Reg = REGG
   608  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   609  	if c.cursym.CFunc() {
   610  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   611  	}
   612  	p.To.Type = obj.TYPE_REG
   613  	p.To.Reg = REG_R1
   614  
   615  	// Mark the stack bound check and morestack call async nonpreemptible.
   616  	// If we get preempted here, when resumed the preemption request is
   617  	// cleared, but we'll still call morestack, which will double the stack
   618  	// unnecessarily. See issue #35470.
   619  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   620  
   621  	if framesize <= abi.StackSmall {
   622  		// small stack: SP < stackguard
   623  		//	CMP	stackguard, SP
   624  		p = obj.Appendp(p, c.newprog)
   625  
   626  		p.As = ACMP
   627  		p.From.Type = obj.TYPE_REG
   628  		p.From.Reg = REG_R1
   629  		p.Reg = REGSP
   630  	} else if framesize <= abi.StackBig {
   631  		// large stack: SP-framesize < stackguard-StackSmall
   632  		//	MOVW $-(framesize-StackSmall)(SP), R2
   633  		//	CMP stackguard, R2
   634  		p = obj.Appendp(p, c.newprog)
   635  
   636  		p.As = AMOVW
   637  		p.From.Type = obj.TYPE_ADDR
   638  		p.From.Reg = REGSP
   639  		p.From.Offset = -(int64(framesize) - abi.StackSmall)
   640  		p.To.Type = obj.TYPE_REG
   641  		p.To.Reg = REG_R2
   642  
   643  		p = obj.Appendp(p, c.newprog)
   644  		p.As = ACMP
   645  		p.From.Type = obj.TYPE_REG
   646  		p.From.Reg = REG_R1
   647  		p.Reg = REG_R2
   648  	} else {
   649  		// Such a large stack we need to protect against underflow.
   650  		// The runtime guarantees SP > objabi.StackBig, but
   651  		// framesize is large enough that SP-framesize may
   652  		// underflow, causing a direct comparison with the
   653  		// stack guard to incorrectly succeed. We explicitly
   654  		// guard against underflow.
   655  		//
   656  		//	// Try subtracting from SP and check for underflow.
   657  		//	// If this underflows, it sets C to 0.
   658  		//	SUB.S $(framesize-StackSmall), SP, R2
   659  		//	// If C is 1 (unsigned >=), compare with guard.
   660  		//	CMP.HS stackguard, R2
   661  
   662  		p = obj.Appendp(p, c.newprog)
   663  		p.As = ASUB
   664  		p.Scond = C_SBIT
   665  		p.From.Type = obj.TYPE_CONST
   666  		p.From.Offset = int64(framesize) - abi.StackSmall
   667  		p.Reg = REGSP
   668  		p.To.Type = obj.TYPE_REG
   669  		p.To.Reg = REG_R2
   670  
   671  		p = obj.Appendp(p, c.newprog)
   672  		p.As = ACMP
   673  		p.Scond = C_SCOND_HS
   674  		p.From.Type = obj.TYPE_REG
   675  		p.From.Reg = REG_R1
   676  		p.Reg = REG_R2
   677  	}
   678  
   679  	// BLS call-to-morestack (C is 0 or Z is 1)
   680  	bls := obj.Appendp(p, c.newprog)
   681  	bls.As = ABLS
   682  	bls.To.Type = obj.TYPE_BRANCH
   683  
   684  	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
   685  
   686  	var last *obj.Prog
   687  	for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
   688  	}
   689  
   690  	// Now we are at the end of the function, but logically
   691  	// we are still in function prologue. We need to fix the
   692  	// SP data and PCDATA.
   693  	spfix := obj.Appendp(last, c.newprog)
   694  	spfix.As = obj.ANOP
   695  	spfix.Spadj = -framesize
   696  
   697  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
   698  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
   699  
   700  	// MOVW	LR, R3
   701  	movw := obj.Appendp(pcdata, c.newprog)
   702  	movw.As = AMOVW
   703  	movw.From.Type = obj.TYPE_REG
   704  	movw.From.Reg = REGLINK
   705  	movw.To.Type = obj.TYPE_REG
   706  	movw.To.Reg = REG_R3
   707  
   708  	bls.To.SetTarget(movw)
   709  
   710  	// BL runtime.morestack
   711  	call := obj.Appendp(movw, c.newprog)
   712  	call.As = obj.ACALL
   713  	call.To.Type = obj.TYPE_BRANCH
   714  	morestack := "runtime.morestack"
   715  	switch {
   716  	case c.cursym.CFunc():
   717  		morestack = "runtime.morestackc"
   718  	case !c.cursym.Func().Text.From.Sym.NeedCtxt():
   719  		morestack = "runtime.morestack_noctxt"
   720  	}
   721  	call.To.Sym = c.ctxt.Lookup(morestack)
   722  
   723  	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
   724  
   725  	// B start
   726  	b := obj.Appendp(pcdata, c.newprog)
   727  	b.As = obj.AJMP
   728  	b.To.Type = obj.TYPE_BRANCH
   729  	b.To.SetTarget(startPred.Link)
   730  	b.Spadj = +framesize
   731  
   732  	return end
   733  }
   734  
   735  var unaryDst = map[obj.As]bool{
   736  	ASWI:  true,
   737  	AWORD: true,
   738  }
   739  
   740  var Linkarm = obj.LinkArch{
   741  	Arch:           sys.ArchARM,
   742  	Init:           buildop,
   743  	Preprocess:     preprocess,
   744  	Assemble:       span5,
   745  	Progedit:       progedit,
   746  	UnaryDst:       unaryDst,
   747  	DWARFRegisters: ARMDWARFRegisters,
   748  }
   749  

View as plain text