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