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