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