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