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