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