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