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