github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/obj/arm/obj5.go (about)

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