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