github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/src/cmd/internal/obj/arm/obj5.go (about)

     1  // Derived from Inferno utils/5c/swt.c
     2  // http://code.google.com/p/inferno-os/source/browse/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  	"encoding/binary"
    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 procesors.
    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 rewriten 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 o int
   346  	var p1 *obj.Prog
   347  	var p2 *obj.Prog
   348  	var q2 *obj.Prog
   349  	for p := cursym.Text; p != nil; p = p.Link {
   350  		o = int(p.As)
   351  		switch o {
   352  		case obj.ATEXT:
   353  			autosize = int32(p.To.Offset + 4)
   354  			if autosize <= 4 {
   355  				if cursym.Text.Mark&LEAF != 0 {
   356  					p.To.Offset = -4
   357  					autosize = 0
   358  				}
   359  			}
   360  
   361  			if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
   362  				if ctxt.Debugvlog != 0 {
   363  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
   364  					ctxt.Bso.Flush()
   365  				}
   366  
   367  				cursym.Text.Mark |= LEAF
   368  			}
   369  
   370  			if cursym.Text.Mark&LEAF != 0 {
   371  				cursym.Leaf = 1
   372  				if autosize == 0 {
   373  					break
   374  				}
   375  			}
   376  
   377  			if p.From3.Offset&obj.NOSPLIT == 0 {
   378  				p = stacksplit(ctxt, p, autosize) // emit split check
   379  			}
   380  
   381  			// MOVW.W		R14,$-autosize(SP)
   382  			p = obj.Appendp(ctxt, p)
   383  
   384  			p.As = AMOVW
   385  			p.Scond |= C_WBIT
   386  			p.From.Type = obj.TYPE_REG
   387  			p.From.Reg = REGLINK
   388  			p.To.Type = obj.TYPE_MEM
   389  			p.To.Offset = int64(-autosize)
   390  			p.To.Reg = REGSP
   391  			p.Spadj = autosize
   392  
   393  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   394  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   395  				//
   396  				//	MOVW g_panic(g), R1
   397  				//	CMP $0, R1
   398  				//	B.EQ end
   399  				//	MOVW panic_argp(R1), R2
   400  				//	ADD $(autosize+4), R13, R3
   401  				//	CMP R2, R3
   402  				//	B.NE end
   403  				//	ADD $4, R13, R4
   404  				//	MOVW R4, panic_argp(R1)
   405  				// end:
   406  				//	NOP
   407  				//
   408  				// The NOP is needed to give the jumps somewhere to land.
   409  				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
   410  
   411  				p = obj.Appendp(ctxt, p)
   412  
   413  				p.As = AMOVW
   414  				p.From.Type = obj.TYPE_MEM
   415  				p.From.Reg = REGG
   416  				p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   417  				p.To.Type = obj.TYPE_REG
   418  				p.To.Reg = REG_R1
   419  
   420  				p = obj.Appendp(ctxt, p)
   421  				p.As = ACMP
   422  				p.From.Type = obj.TYPE_CONST
   423  				p.From.Offset = 0
   424  				p.Reg = REG_R1
   425  
   426  				p = obj.Appendp(ctxt, p)
   427  				p.As = ABEQ
   428  				p.To.Type = obj.TYPE_BRANCH
   429  				p1 = p
   430  
   431  				p = obj.Appendp(ctxt, p)
   432  				p.As = AMOVW
   433  				p.From.Type = obj.TYPE_MEM
   434  				p.From.Reg = REG_R1
   435  				p.From.Offset = 0 // Panic.argp
   436  				p.To.Type = obj.TYPE_REG
   437  				p.To.Reg = REG_R2
   438  
   439  				p = obj.Appendp(ctxt, p)
   440  				p.As = AADD
   441  				p.From.Type = obj.TYPE_CONST
   442  				p.From.Offset = int64(autosize) + 4
   443  				p.Reg = REG_R13
   444  				p.To.Type = obj.TYPE_REG
   445  				p.To.Reg = REG_R3
   446  
   447  				p = obj.Appendp(ctxt, p)
   448  				p.As = ACMP
   449  				p.From.Type = obj.TYPE_REG
   450  				p.From.Reg = REG_R2
   451  				p.Reg = REG_R3
   452  
   453  				p = obj.Appendp(ctxt, p)
   454  				p.As = ABNE
   455  				p.To.Type = obj.TYPE_BRANCH
   456  				p2 = p
   457  
   458  				p = obj.Appendp(ctxt, p)
   459  				p.As = AADD
   460  				p.From.Type = obj.TYPE_CONST
   461  				p.From.Offset = 4
   462  				p.Reg = REG_R13
   463  				p.To.Type = obj.TYPE_REG
   464  				p.To.Reg = REG_R4
   465  
   466  				p = obj.Appendp(ctxt, p)
   467  				p.As = AMOVW
   468  				p.From.Type = obj.TYPE_REG
   469  				p.From.Reg = REG_R4
   470  				p.To.Type = obj.TYPE_MEM
   471  				p.To.Reg = REG_R1
   472  				p.To.Offset = 0 // Panic.argp
   473  
   474  				p = obj.Appendp(ctxt, p)
   475  
   476  				p.As = obj.ANOP
   477  				p1.Pcond = p
   478  				p2.Pcond = p
   479  			}
   480  
   481  		case obj.ARET:
   482  			obj.Nocache(p)
   483  			if cursym.Text.Mark&LEAF != 0 {
   484  				if autosize == 0 {
   485  					p.As = AB
   486  					p.From = obj.Addr{}
   487  					if p.To.Sym != nil { // retjmp
   488  						p.To.Type = obj.TYPE_BRANCH
   489  					} else {
   490  						p.To.Type = obj.TYPE_MEM
   491  						p.To.Offset = 0
   492  						p.To.Reg = REGLINK
   493  					}
   494  
   495  					break
   496  				}
   497  			}
   498  
   499  			p.As = AMOVW
   500  			p.Scond |= C_PBIT
   501  			p.From.Type = obj.TYPE_MEM
   502  			p.From.Offset = int64(autosize)
   503  			p.From.Reg = REGSP
   504  			p.To.Type = obj.TYPE_REG
   505  			p.To.Reg = REGPC
   506  
   507  			// If there are instructions following
   508  			// this ARET, they come from a branch
   509  			// with the same stackframe, so no spadj.
   510  			if p.To.Sym != nil { // retjmp
   511  				p.To.Reg = REGLINK
   512  				q2 = obj.Appendp(ctxt, p)
   513  				q2.As = AB
   514  				q2.To.Type = obj.TYPE_BRANCH
   515  				q2.To.Sym = p.To.Sym
   516  				p.To.Sym = nil
   517  				p = q2
   518  			}
   519  
   520  		case AADD:
   521  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   522  				p.Spadj = int32(-p.From.Offset)
   523  			}
   524  
   525  		case ASUB:
   526  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   527  				p.Spadj = int32(p.From.Offset)
   528  			}
   529  
   530  		case ADIV, ADIVU, AMOD, AMODU:
   531  			if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
   532  				ctxt.Diag("cannot divide in NOSPLIT function")
   533  			}
   534  			if ctxt.Debugdivmod != 0 {
   535  				break
   536  			}
   537  			if p.From.Type != obj.TYPE_REG {
   538  				break
   539  			}
   540  			if p.To.Type != obj.TYPE_REG {
   541  				break
   542  			}
   543  
   544  			// Make copy because we overwrite p below.
   545  			q1 := *p
   546  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   547  				ctxt.Diag("div already using REGTMP: %v", p)
   548  			}
   549  
   550  			/* MOV m(g),REGTMP */
   551  			p.As = AMOVW
   552  			p.Lineno = q1.Lineno
   553  			p.From.Type = obj.TYPE_MEM
   554  			p.From.Reg = REGG
   555  			p.From.Offset = 6 * 4 // offset of g.m
   556  			p.Reg = 0
   557  			p.To.Type = obj.TYPE_REG
   558  			p.To.Reg = REGTMP
   559  
   560  			/* MOV a,m_divmod(REGTMP) */
   561  			p = obj.Appendp(ctxt, p)
   562  			p.As = AMOVW
   563  			p.Lineno = q1.Lineno
   564  			p.From.Type = obj.TYPE_REG
   565  			p.From.Reg = q1.From.Reg
   566  			p.To.Type = obj.TYPE_MEM
   567  			p.To.Reg = REGTMP
   568  			p.To.Offset = 8 * 4 // offset of m.divmod
   569  
   570  			/* MOV b,REGTMP */
   571  			p = obj.Appendp(ctxt, p)
   572  			p.As = AMOVW
   573  			p.Lineno = q1.Lineno
   574  			p.From.Type = obj.TYPE_REG
   575  			p.From.Reg = q1.Reg
   576  			if q1.Reg == 0 {
   577  				p.From.Reg = q1.To.Reg
   578  			}
   579  			p.To.Type = obj.TYPE_REG
   580  			p.To.Reg = REGTMP
   581  			p.To.Offset = 0
   582  
   583  			/* CALL appropriate */
   584  			p = obj.Appendp(ctxt, p)
   585  			p.As = ABL
   586  			p.Lineno = q1.Lineno
   587  			p.To.Type = obj.TYPE_BRANCH
   588  			switch o {
   589  			case ADIV:
   590  				p.To.Sym = ctxt.Sym_div
   591  
   592  			case ADIVU:
   593  				p.To.Sym = ctxt.Sym_divu
   594  
   595  			case AMOD:
   596  				p.To.Sym = ctxt.Sym_mod
   597  
   598  			case AMODU:
   599  				p.To.Sym = ctxt.Sym_modu
   600  			}
   601  
   602  			/* MOV REGTMP, b */
   603  			p = obj.Appendp(ctxt, p)
   604  			p.As = AMOVW
   605  			p.Lineno = q1.Lineno
   606  			p.From.Type = obj.TYPE_REG
   607  			p.From.Reg = REGTMP
   608  			p.From.Offset = 0
   609  			p.To.Type = obj.TYPE_REG
   610  			p.To.Reg = q1.To.Reg
   611  
   612  		case AMOVW:
   613  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   614  				p.Spadj = int32(-p.To.Offset)
   615  			}
   616  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   617  				p.Spadj = int32(-p.From.Offset)
   618  			}
   619  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   620  				p.Spadj = int32(-p.From.Offset)
   621  			}
   622  		}
   623  	}
   624  }
   625  
   626  func isfloatreg(a *obj.Addr) bool {
   627  	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
   628  }
   629  
   630  func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
   631  	if ctxt.Goarm > 5 {
   632  		return
   633  	}
   634  
   635  	symsfloat := obj.Linklookup(ctxt, "_sfloat", 0)
   636  
   637  	wasfloat := 0
   638  	for p := cursym.Text; p != nil; p = p.Link {
   639  		if p.Pcond != nil {
   640  			p.Pcond.Mark |= LABEL
   641  		}
   642  	}
   643  	var next *obj.Prog
   644  	for p := cursym.Text; p != nil; p = p.Link {
   645  		switch p.As {
   646  		case AMOVW:
   647  			if isfloatreg(&p.To) || isfloatreg(&p.From) {
   648  				goto soft
   649  			}
   650  			goto notsoft
   651  
   652  		case AMOVWD,
   653  			AMOVWF,
   654  			AMOVDW,
   655  			AMOVFW,
   656  			AMOVFD,
   657  			AMOVDF,
   658  			AMOVF,
   659  			AMOVD,
   660  			ACMPF,
   661  			ACMPD,
   662  			AADDF,
   663  			AADDD,
   664  			ASUBF,
   665  			ASUBD,
   666  			AMULF,
   667  			AMULD,
   668  			ADIVF,
   669  			ADIVD,
   670  			ASQRTF,
   671  			ASQRTD,
   672  			AABSF,
   673  			AABSD:
   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 != 0 {
   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  	// MOVW	LR, R3
   807  	movw := obj.Appendp(ctxt, last)
   808  	movw.As = AMOVW
   809  	movw.From.Type = obj.TYPE_REG
   810  	movw.From.Reg = REGLINK
   811  	movw.To.Type = obj.TYPE_REG
   812  	movw.To.Reg = REG_R3
   813  
   814  	bls.Pcond = movw
   815  
   816  	// BL runtime.morestack
   817  	call := obj.Appendp(ctxt, movw)
   818  	call.As = obj.ACALL
   819  	call.To.Type = obj.TYPE_BRANCH
   820  	morestack := "runtime.morestack"
   821  	switch {
   822  	case ctxt.Cursym.Cfunc != 0:
   823  		morestack = "runtime.morestackc"
   824  	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
   825  		morestack = "runtime.morestack_noctxt"
   826  	}
   827  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   828  
   829  	// B start
   830  	b := obj.Appendp(ctxt, call)
   831  	b.As = obj.AJMP
   832  	b.To.Type = obj.TYPE_BRANCH
   833  	b.Pcond = ctxt.Cursym.Text.Link
   834  
   835  	return bls
   836  }
   837  
   838  func initdiv(ctxt *obj.Link) {
   839  	if ctxt.Sym_div != nil {
   840  		return
   841  	}
   842  	ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0)
   843  	ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0)
   844  	ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0)
   845  	ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0)
   846  }
   847  
   848  func follow(ctxt *obj.Link, s *obj.LSym) {
   849  	ctxt.Cursym = s
   850  
   851  	firstp := ctxt.NewProg()
   852  	lastp := firstp
   853  	xfol(ctxt, s.Text, &lastp)
   854  	lastp.Link = nil
   855  	s.Text = firstp.Link
   856  }
   857  
   858  func relinv(a int) int {
   859  	switch a {
   860  	case ABEQ:
   861  		return ABNE
   862  	case ABNE:
   863  		return ABEQ
   864  	case ABCS:
   865  		return ABCC
   866  	case ABHS:
   867  		return ABLO
   868  	case ABCC:
   869  		return ABCS
   870  	case ABLO:
   871  		return ABHS
   872  	case ABMI:
   873  		return ABPL
   874  	case ABPL:
   875  		return ABMI
   876  	case ABVS:
   877  		return ABVC
   878  	case ABVC:
   879  		return ABVS
   880  	case ABHI:
   881  		return ABLS
   882  	case ABLS:
   883  		return ABHI
   884  	case ABGE:
   885  		return ABLT
   886  	case ABLT:
   887  		return ABGE
   888  	case ABGT:
   889  		return ABLE
   890  	case ABLE:
   891  		return ABGT
   892  	}
   893  
   894  	log.Fatalf("unknown relation: %s", Anames[a])
   895  	return 0
   896  }
   897  
   898  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   899  	var q *obj.Prog
   900  	var r *obj.Prog
   901  	var a int
   902  	var i int
   903  
   904  loop:
   905  	if p == nil {
   906  		return
   907  	}
   908  	a = int(p.As)
   909  	if a == AB {
   910  		q = p.Pcond
   911  		if q != nil && q.As != obj.ATEXT {
   912  			p.Mark |= FOLL
   913  			p = q
   914  			if p.Mark&FOLL == 0 {
   915  				goto loop
   916  			}
   917  		}
   918  	}
   919  
   920  	if p.Mark&FOLL != 0 {
   921  		i = 0
   922  		q = p
   923  		for ; i < 4; i, q = i+1, q.Link {
   924  			if q == *last || q == nil {
   925  				break
   926  			}
   927  			a = int(q.As)
   928  			if a == obj.ANOP {
   929  				i--
   930  				continue
   931  			}
   932  
   933  			if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   934  				goto copy
   935  			}
   936  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   937  				continue
   938  			}
   939  			if a != ABEQ && a != ABNE {
   940  				continue
   941  			}
   942  
   943  		copy:
   944  			for {
   945  				r = ctxt.NewProg()
   946  				*r = *p
   947  				if r.Mark&FOLL == 0 {
   948  					fmt.Printf("can't happen 1\n")
   949  				}
   950  				r.Mark |= FOLL
   951  				if p != q {
   952  					p = p.Link
   953  					(*last).Link = r
   954  					*last = r
   955  					continue
   956  				}
   957  
   958  				(*last).Link = r
   959  				*last = r
   960  				if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   961  					return
   962  				}
   963  				r.As = ABNE
   964  				if a == ABNE {
   965  					r.As = ABEQ
   966  				}
   967  				r.Pcond = p.Link
   968  				r.Link = p.Pcond
   969  				if r.Link.Mark&FOLL == 0 {
   970  					xfol(ctxt, r.Link, last)
   971  				}
   972  				if r.Pcond.Mark&FOLL == 0 {
   973  					fmt.Printf("can't happen 2\n")
   974  				}
   975  				return
   976  			}
   977  		}
   978  
   979  		a = AB
   980  		q = ctxt.NewProg()
   981  		q.As = int16(a)
   982  		q.Lineno = p.Lineno
   983  		q.To.Type = obj.TYPE_BRANCH
   984  		q.To.Offset = p.Pc
   985  		q.Pcond = p
   986  		p = q
   987  	}
   988  
   989  	p.Mark |= FOLL
   990  	(*last).Link = p
   991  	*last = p
   992  	if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   993  		return
   994  	}
   995  
   996  	if p.Pcond != nil {
   997  		if a != ABL && a != ABX && p.Link != nil {
   998  			q = obj.Brchain(ctxt, p.Link)
   999  			if a != obj.ATEXT {
  1000  				if q != nil && (q.Mark&FOLL != 0) {
  1001  					p.As = int16(relinv(a))
  1002  					p.Link = p.Pcond
  1003  					p.Pcond = q
  1004  				}
  1005  			}
  1006  
  1007  			xfol(ctxt, p.Link, last)
  1008  			q = obj.Brchain(ctxt, p.Pcond)
  1009  			if q == nil {
  1010  				q = p.Pcond
  1011  			}
  1012  			if q.Mark&FOLL != 0 {
  1013  				p.Pcond = q
  1014  				return
  1015  			}
  1016  
  1017  			p = q
  1018  			goto loop
  1019  		}
  1020  	}
  1021  
  1022  	p = p.Link
  1023  	goto loop
  1024  }
  1025  
  1026  var unaryDst = map[int]bool{
  1027  	ASWI:  true,
  1028  	AWORD: true,
  1029  }
  1030  
  1031  var Linkarm = obj.LinkArch{
  1032  	ByteOrder:  binary.LittleEndian,
  1033  	Name:       "arm",
  1034  	Thechar:    '5',
  1035  	Preprocess: preprocess,
  1036  	Assemble:   span5,
  1037  	Follow:     follow,
  1038  	Progedit:   progedit,
  1039  	UnaryDst:   unaryDst,
  1040  	Minlc:      4,
  1041  	Ptrsize:    4,
  1042  	Regsize:    4,
  1043  }