github.com/go-asm/go@v1.21.1-0.20240213172139-40c5ead50c48/cmd/obj/arm/obj5.go (about)

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