github.com/FenixAra/go@v0.0.0-20170127160404-96ea0918e670/src/cmd/internal/obj/arm/obj5.go (about)

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