Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/internal/obj/arm

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

View as plain text