github.com/c9s/go@v0.0.0-20180120015821-984e81f64e0c/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/objabi"
    36  	"cmd/internal/sys"
    37  )
    38  
    39  var progedit_tlsfallback *obj.LSym
    40  
    41  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    42  	p.From.Class = 0
    43  	p.To.Class = 0
    44  
    45  	c := ctxt5{ctxt: ctxt, newprog: newprog}
    46  
    47  	// Rewrite B/BL to symbol as TYPE_BRANCH.
    48  	switch p.As {
    49  	case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
    50  		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
    51  			p.To.Type = obj.TYPE_BRANCH
    52  		}
    53  	}
    54  
    55  	// Replace TLS register fetches on older ARM processors.
    56  	switch p.As {
    57  	// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
    58  	case AMRC:
    59  		if p.To.Offset&0xffff0fff == 0xee1d0f70 {
    60  			// Because the instruction might be rewritten to a BL which returns in R0
    61  			// the register must be zero.
    62  			if p.To.Offset&0xf000 != 0 {
    63  				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
    64  			}
    65  
    66  			if objabi.GOARM < 7 {
    67  				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
    68  				if progedit_tlsfallback == nil {
    69  					progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
    70  				}
    71  
    72  				// MOVW	LR, R11
    73  				p.As = AMOVW
    74  
    75  				p.From.Type = obj.TYPE_REG
    76  				p.From.Reg = REGLINK
    77  				p.To.Type = obj.TYPE_REG
    78  				p.To.Reg = REGTMP
    79  
    80  				// BL	runtime.read_tls_fallback(SB)
    81  				p = obj.Appendp(p, newprog)
    82  
    83  				p.As = ABL
    84  				p.To.Type = obj.TYPE_BRANCH
    85  				p.To.Sym = progedit_tlsfallback
    86  				p.To.Offset = 0
    87  
    88  				// MOVW	R11, LR
    89  				p = obj.Appendp(p, newprog)
    90  
    91  				p.As = AMOVW
    92  				p.From.Type = obj.TYPE_REG
    93  				p.From.Reg = REGTMP
    94  				p.To.Type = obj.TYPE_REG
    95  				p.To.Reg = REGLINK
    96  				break
    97  			}
    98  		}
    99  
   100  		// Otherwise, MRC/MCR instructions need no further treatment.
   101  		p.As = AWORD
   102  	}
   103  
   104  	// Rewrite float constants to values stored in memory.
   105  	switch p.As {
   106  	case AMOVF:
   107  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   108  			f32 := float32(p.From.Val.(float64))
   109  			p.From.Type = obj.TYPE_MEM
   110  			p.From.Sym = ctxt.Float32Sym(f32)
   111  			p.From.Name = obj.NAME_EXTERN
   112  			p.From.Offset = 0
   113  		}
   114  
   115  	case AMOVD:
   116  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
   117  			p.From.Type = obj.TYPE_MEM
   118  			p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
   119  			p.From.Name = obj.NAME_EXTERN
   120  			p.From.Offset = 0
   121  		}
   122  	}
   123  
   124  	if ctxt.Flag_dynlink {
   125  		c.rewriteToUseGot(p)
   126  	}
   127  }
   128  
   129  // Rewrite p, if necessary, to access global data via the global offset table.
   130  func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
   131  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   132  		//     ADUFFxxx $offset
   133  		// becomes
   134  		//     MOVW runtime.duffxxx@GOT, R9
   135  		//     ADD $offset, R9
   136  		//     CALL (R9)
   137  		var sym *obj.LSym
   138  		if p.As == obj.ADUFFZERO {
   139  			sym = c.ctxt.Lookup("runtime.duffzero")
   140  		} else {
   141  			sym = c.ctxt.Lookup("runtime.duffcopy")
   142  		}
   143  		offset := p.To.Offset
   144  		p.As = AMOVW
   145  		p.From.Type = obj.TYPE_MEM
   146  		p.From.Name = obj.NAME_GOTREF
   147  		p.From.Sym = sym
   148  		p.To.Type = obj.TYPE_REG
   149  		p.To.Reg = REG_R9
   150  		p.To.Name = obj.NAME_NONE
   151  		p.To.Offset = 0
   152  		p.To.Sym = nil
   153  		p1 := obj.Appendp(p, c.newprog)
   154  		p1.As = AADD
   155  		p1.From.Type = obj.TYPE_CONST
   156  		p1.From.Offset = offset
   157  		p1.To.Type = obj.TYPE_REG
   158  		p1.To.Reg = REG_R9
   159  		p2 := obj.Appendp(p1, c.newprog)
   160  		p2.As = obj.ACALL
   161  		p2.To.Type = obj.TYPE_MEM
   162  		p2.To.Reg = REG_R9
   163  		return
   164  	}
   165  
   166  	// We only care about global data: NAME_EXTERN means a global
   167  	// symbol in the Go sense, and p.Sym.Local is true for a few
   168  	// internally defined symbols.
   169  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   170  		// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
   171  		// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
   172  		if p.As != AMOVW {
   173  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   174  		}
   175  		if p.To.Type != obj.TYPE_REG {
   176  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   177  		}
   178  		p.From.Type = obj.TYPE_MEM
   179  		p.From.Name = obj.NAME_GOTREF
   180  		if p.From.Offset != 0 {
   181  			q := obj.Appendp(p, c.newprog)
   182  			q.As = AADD
   183  			q.From.Type = obj.TYPE_CONST
   184  			q.From.Offset = p.From.Offset
   185  			q.To = p.To
   186  			p.From.Offset = 0
   187  		}
   188  	}
   189  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   190  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   191  	}
   192  	var source *obj.Addr
   193  	// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
   194  	// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
   195  	// An addition may be inserted between the two MOVs if there is an offset.
   196  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   197  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   198  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   199  		}
   200  		source = &p.From
   201  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   202  		source = &p.To
   203  	} else {
   204  		return
   205  	}
   206  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   207  		return
   208  	}
   209  	if source.Sym.Type == objabi.STLSBSS {
   210  		return
   211  	}
   212  	if source.Type != obj.TYPE_MEM {
   213  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   214  	}
   215  	p1 := obj.Appendp(p, c.newprog)
   216  	p2 := obj.Appendp(p1, c.newprog)
   217  
   218  	p1.As = AMOVW
   219  	p1.From.Type = obj.TYPE_MEM
   220  	p1.From.Sym = source.Sym
   221  	p1.From.Name = obj.NAME_GOTREF
   222  	p1.To.Type = obj.TYPE_REG
   223  	p1.To.Reg = REG_R9
   224  
   225  	p2.As = p.As
   226  	p2.From = p.From
   227  	p2.To = p.To
   228  	if p.From.Name == obj.NAME_EXTERN {
   229  		p2.From.Reg = REG_R9
   230  		p2.From.Name = obj.NAME_NONE
   231  		p2.From.Sym = nil
   232  	} else if p.To.Name == obj.NAME_EXTERN {
   233  		p2.To.Reg = REG_R9
   234  		p2.To.Name = obj.NAME_NONE
   235  		p2.To.Sym = nil
   236  	} else {
   237  		return
   238  	}
   239  	obj.Nopout(p)
   240  }
   241  
   242  // Prog.mark
   243  const (
   244  	FOLL  = 1 << 0
   245  	LABEL = 1 << 1
   246  	LEAF  = 1 << 2
   247  )
   248  
   249  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   250  	autosize := int32(0)
   251  
   252  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   253  		return
   254  	}
   255  
   256  	c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
   257  
   258  	c.softfloat()
   259  
   260  	p := c.cursym.Func.Text
   261  	autoffset := int32(p.To.Offset)
   262  	if autoffset < 0 {
   263  		autoffset = 0
   264  	}
   265  	cursym.Func.Locals = autoffset
   266  	cursym.Func.Args = p.To.Val.(int32)
   267  
   268  	/*
   269  	 * find leaf subroutines
   270  	 * strip NOPs
   271  	 * expand RET
   272  	 * expand BECOME pseudo
   273  	 */
   274  	var q1 *obj.Prog
   275  	var q *obj.Prog
   276  	for p := cursym.Func.Text; p != nil; p = p.Link {
   277  		switch p.As {
   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  			cursym.Func.Text.Mark &^= LEAF
   287  			continue
   288  
   289  		case obj.ANOP:
   290  			q1 = p.Link
   291  			q.Link = q1 /* q is non-nop */
   292  			if q1 != nil {
   293  				q1.Mark |= p.Mark
   294  			}
   295  			continue
   296  
   297  		case ABL,
   298  			ABX,
   299  			obj.ADUFFZERO,
   300  			obj.ADUFFCOPY:
   301  			cursym.Func.Text.Mark &^= LEAF
   302  			fallthrough
   303  
   304  		case AB,
   305  			ABEQ,
   306  			ABNE,
   307  			ABCS,
   308  			ABHS,
   309  			ABCC,
   310  			ABLO,
   311  			ABMI,
   312  			ABPL,
   313  			ABVS,
   314  			ABVC,
   315  			ABHI,
   316  			ABLS,
   317  			ABGE,
   318  			ABLT,
   319  			ABGT,
   320  			ABLE:
   321  			q1 = p.Pcond
   322  			if q1 != nil {
   323  				for q1.As == obj.ANOP {
   324  					q1 = q1.Link
   325  					p.Pcond = q1
   326  				}
   327  			}
   328  		}
   329  
   330  		q = p
   331  	}
   332  
   333  	var q2 *obj.Prog
   334  	for p := cursym.Func.Text; p != nil; p = p.Link {
   335  		o := p.As
   336  		switch o {
   337  		case obj.ATEXT:
   338  			autosize = int32(p.To.Offset + 4)
   339  			if autosize <= 4 {
   340  				if cursym.Func.Text.Mark&LEAF != 0 {
   341  					p.To.Offset = -4
   342  					autosize = 0
   343  				}
   344  			}
   345  
   346  			if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
   347  				if ctxt.Debugvlog {
   348  					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
   349  				}
   350  
   351  				cursym.Func.Text.Mark |= LEAF
   352  			}
   353  
   354  			if cursym.Func.Text.Mark&LEAF != 0 {
   355  				cursym.Set(obj.AttrLeaf, true)
   356  				if autosize == 0 {
   357  					break
   358  				}
   359  			}
   360  
   361  			if !p.From.Sym.NoSplit() {
   362  				p = c.stacksplit(p, autosize) // emit split check
   363  			}
   364  
   365  			// MOVW.W		R14,$-autosize(SP)
   366  			p = obj.Appendp(p, c.newprog)
   367  
   368  			p.As = AMOVW
   369  			p.Scond |= C_WBIT
   370  			p.From.Type = obj.TYPE_REG
   371  			p.From.Reg = REGLINK
   372  			p.To.Type = obj.TYPE_MEM
   373  			p.To.Offset = int64(-autosize)
   374  			p.To.Reg = REGSP
   375  			p.Spadj = autosize
   376  
   377  			if cursym.Func.Text.From.Sym.Wrapper() {
   378  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   379  				//
   380  				//	MOVW g_panic(g), R1
   381  				//	CMP  $0, R1
   382  				//	B.NE checkargp
   383  				// end:
   384  				//	NOP
   385  				// ... function ...
   386  				// checkargp:
   387  				//	MOVW panic_argp(R1), R2
   388  				//	ADD  $(autosize+4), R13, R3
   389  				//	CMP  R2, R3
   390  				//	B.NE end
   391  				//	ADD  $4, R13, R4
   392  				//	MOVW R4, panic_argp(R1)
   393  				//	B    end
   394  				//
   395  				// The NOP is needed to give the jumps somewhere to land.
   396  				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
   397  
   398  				p = obj.Appendp(p, newprog)
   399  				p.As = AMOVW
   400  				p.From.Type = obj.TYPE_MEM
   401  				p.From.Reg = REGG
   402  				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   403  				p.To.Type = obj.TYPE_REG
   404  				p.To.Reg = REG_R1
   405  
   406  				p = obj.Appendp(p, newprog)
   407  				p.As = ACMP
   408  				p.From.Type = obj.TYPE_CONST
   409  				p.From.Offset = 0
   410  				p.Reg = REG_R1
   411  
   412  				// B.NE checkargp
   413  				bne := obj.Appendp(p, newprog)
   414  				bne.As = ABNE
   415  				bne.To.Type = obj.TYPE_BRANCH
   416  
   417  				// end: NOP
   418  				end := obj.Appendp(bne, newprog)
   419  				end.As = obj.ANOP
   420  
   421  				// find end of function
   422  				var last *obj.Prog
   423  				for last = end; last.Link != nil; last = last.Link {
   424  				}
   425  
   426  				// MOVW panic_argp(R1), R2
   427  				mov := obj.Appendp(last, newprog)
   428  				mov.As = AMOVW
   429  				mov.From.Type = obj.TYPE_MEM
   430  				mov.From.Reg = REG_R1
   431  				mov.From.Offset = 0 // Panic.argp
   432  				mov.To.Type = obj.TYPE_REG
   433  				mov.To.Reg = REG_R2
   434  
   435  				// B.NE branch target is MOVW above
   436  				bne.Pcond = mov
   437  
   438  				// ADD $(autosize+4), R13, R3
   439  				p = obj.Appendp(mov, newprog)
   440  				p.As = AADD
   441  				p.From.Type = obj.TYPE_CONST
   442  				p.From.Offset = int64(autosize) + 4
   443  				p.Reg = REG_R13
   444  				p.To.Type = obj.TYPE_REG
   445  				p.To.Reg = REG_R3
   446  
   447  				// CMP R2, R3
   448  				p = obj.Appendp(p, newprog)
   449  				p.As = ACMP
   450  				p.From.Type = obj.TYPE_REG
   451  				p.From.Reg = REG_R2
   452  				p.Reg = REG_R3
   453  
   454  				// B.NE end
   455  				p = obj.Appendp(p, newprog)
   456  				p.As = ABNE
   457  				p.To.Type = obj.TYPE_BRANCH
   458  				p.Pcond = end
   459  
   460  				// ADD $4, R13, R4
   461  				p = obj.Appendp(p, newprog)
   462  				p.As = AADD
   463  				p.From.Type = obj.TYPE_CONST
   464  				p.From.Offset = 4
   465  				p.Reg = REG_R13
   466  				p.To.Type = obj.TYPE_REG
   467  				p.To.Reg = REG_R4
   468  
   469  				// MOVW R4, panic_argp(R1)
   470  				p = obj.Appendp(p, newprog)
   471  				p.As = AMOVW
   472  				p.From.Type = obj.TYPE_REG
   473  				p.From.Reg = REG_R4
   474  				p.To.Type = obj.TYPE_MEM
   475  				p.To.Reg = REG_R1
   476  				p.To.Offset = 0 // Panic.argp
   477  
   478  				// B end
   479  				p = obj.Appendp(p, newprog)
   480  				p.As = AB
   481  				p.To.Type = obj.TYPE_BRANCH
   482  				p.Pcond = end
   483  
   484  				// reset for subsequent passes
   485  				p = end
   486  			}
   487  
   488  		case obj.ARET:
   489  			nocache(p)
   490  			if cursym.Func.Text.Mark&LEAF != 0 {
   491  				if autosize == 0 {
   492  					p.As = AB
   493  					p.From = obj.Addr{}
   494  					if p.To.Sym != nil { // retjmp
   495  						p.To.Type = obj.TYPE_BRANCH
   496  					} else {
   497  						p.To.Type = obj.TYPE_MEM
   498  						p.To.Offset = 0
   499  						p.To.Reg = REGLINK
   500  					}
   501  
   502  					break
   503  				}
   504  			}
   505  
   506  			p.As = AMOVW
   507  			p.Scond |= C_PBIT
   508  			p.From.Type = obj.TYPE_MEM
   509  			p.From.Offset = int64(autosize)
   510  			p.From.Reg = REGSP
   511  			p.To.Type = obj.TYPE_REG
   512  			p.To.Reg = REGPC
   513  
   514  			// If there are instructions following
   515  			// this ARET, they come from a branch
   516  			// with the same stackframe, so no spadj.
   517  			if p.To.Sym != nil { // retjmp
   518  				p.To.Reg = REGLINK
   519  				q2 = obj.Appendp(p, newprog)
   520  				q2.As = AB
   521  				q2.To.Type = obj.TYPE_BRANCH
   522  				q2.To.Sym = p.To.Sym
   523  				p.To.Sym = nil
   524  				p = q2
   525  			}
   526  
   527  		case AADD:
   528  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   529  				p.Spadj = int32(-p.From.Offset)
   530  			}
   531  
   532  		case ASUB:
   533  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   534  				p.Spadj = int32(p.From.Offset)
   535  			}
   536  
   537  		case ADIV, ADIVU, AMOD, AMODU:
   538  			if cursym.Func.Text.From.Sym.NoSplit() {
   539  				ctxt.Diag("cannot divide in NOSPLIT function")
   540  			}
   541  			const debugdivmod = false
   542  			if debugdivmod {
   543  				break
   544  			}
   545  			if p.From.Type != obj.TYPE_REG {
   546  				break
   547  			}
   548  			if p.To.Type != obj.TYPE_REG {
   549  				break
   550  			}
   551  
   552  			// Make copy because we overwrite p below.
   553  			q1 := *p
   554  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   555  				ctxt.Diag("div already using REGTMP: %v", p)
   556  			}
   557  
   558  			/* MOV m(g),REGTMP */
   559  			p.As = AMOVW
   560  			p.Pos = q1.Pos
   561  			p.From.Type = obj.TYPE_MEM
   562  			p.From.Reg = REGG
   563  			p.From.Offset = 6 * 4 // offset of g.m
   564  			p.Reg = 0
   565  			p.To.Type = obj.TYPE_REG
   566  			p.To.Reg = REGTMP
   567  
   568  			/* MOV a,m_divmod(REGTMP) */
   569  			p = obj.Appendp(p, newprog)
   570  			p.As = AMOVW
   571  			p.Pos = q1.Pos
   572  			p.From.Type = obj.TYPE_REG
   573  			p.From.Reg = q1.From.Reg
   574  			p.To.Type = obj.TYPE_MEM
   575  			p.To.Reg = REGTMP
   576  			p.To.Offset = 8 * 4 // offset of m.divmod
   577  
   578  			/* MOV b, R8 */
   579  			p = obj.Appendp(p, newprog)
   580  			p.As = AMOVW
   581  			p.Pos = q1.Pos
   582  			p.From.Type = obj.TYPE_REG
   583  			p.From.Reg = q1.Reg
   584  			if q1.Reg == 0 {
   585  				p.From.Reg = q1.To.Reg
   586  			}
   587  			p.To.Type = obj.TYPE_REG
   588  			p.To.Reg = REG_R8
   589  			p.To.Offset = 0
   590  
   591  			/* CALL appropriate */
   592  			p = obj.Appendp(p, newprog)
   593  			p.As = ABL
   594  			p.Pos = q1.Pos
   595  			p.To.Type = obj.TYPE_BRANCH
   596  			switch o {
   597  			case ADIV:
   598  				p.To.Sym = symdiv
   599  			case ADIVU:
   600  				p.To.Sym = symdivu
   601  			case AMOD:
   602  				p.To.Sym = symmod
   603  			case AMODU:
   604  				p.To.Sym = symmodu
   605  			}
   606  
   607  			/* MOV REGTMP, b */
   608  			p = obj.Appendp(p, newprog)
   609  			p.As = AMOVW
   610  			p.Pos = q1.Pos
   611  			p.From.Type = obj.TYPE_REG
   612  			p.From.Reg = REGTMP
   613  			p.From.Offset = 0
   614  			p.To.Type = obj.TYPE_REG
   615  			p.To.Reg = q1.To.Reg
   616  
   617  		case AMOVW:
   618  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   619  				p.Spadj = int32(-p.To.Offset)
   620  			}
   621  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   622  				p.Spadj = int32(-p.From.Offset)
   623  			}
   624  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   625  				p.Spadj = int32(-p.From.Offset)
   626  			}
   627  		}
   628  	}
   629  }
   630  
   631  func isfloatreg(a *obj.Addr) bool {
   632  	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
   633  }
   634  
   635  func (c *ctxt5) softfloat() {
   636  	if objabi.GOARM > 5 {
   637  		return
   638  	}
   639  
   640  	symsfloat := c.ctxt.Lookup("runtime._sfloat")
   641  
   642  	wasfloat := 0
   643  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   644  		if p.Pcond != nil {
   645  			p.Pcond.Mark |= LABEL
   646  		}
   647  	}
   648  	var next *obj.Prog
   649  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   650  		switch p.As {
   651  		case AMOVW:
   652  			if isfloatreg(&p.To) || isfloatreg(&p.From) {
   653  				goto soft
   654  			}
   655  			goto notsoft
   656  
   657  		case AMOVWD,
   658  			AMOVWF,
   659  			AMOVDW,
   660  			AMOVFW,
   661  			AMOVFD,
   662  			AMOVDF,
   663  			AMOVF,
   664  			AMOVD,
   665  			ACMPF,
   666  			ACMPD,
   667  			AADDF,
   668  			AADDD,
   669  			ASUBF,
   670  			ASUBD,
   671  			AMULF,
   672  			AMULD,
   673  			ADIVF,
   674  			ADIVD,
   675  			ASQRTF,
   676  			ASQRTD,
   677  			AABSF,
   678  			AABSD,
   679  			ANEGF,
   680  			ANEGD:
   681  			goto soft
   682  
   683  		default:
   684  			goto notsoft
   685  		}
   686  
   687  	soft:
   688  		if wasfloat == 0 || (p.Mark&LABEL != 0) {
   689  			next = c.newprog()
   690  			*next = *p
   691  
   692  			// BL runtime·_sfloat(SB)
   693  			*p = obj.Prog{}
   694  			p.Ctxt = c.ctxt
   695  			p.Link = next
   696  			p.As = ABL
   697  			p.To.Type = obj.TYPE_BRANCH
   698  			p.To.Sym = symsfloat
   699  			p.Pos = next.Pos
   700  
   701  			p = next
   702  			wasfloat = 1
   703  		}
   704  
   705  		continue
   706  
   707  	notsoft:
   708  		wasfloat = 0
   709  	}
   710  }
   711  
   712  func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   713  	// MOVW g_stackguard(g), R1
   714  	p = obj.Appendp(p, c.newprog)
   715  
   716  	p.As = AMOVW
   717  	p.From.Type = obj.TYPE_MEM
   718  	p.From.Reg = REGG
   719  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   720  	if c.cursym.CFunc() {
   721  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   722  	}
   723  	p.To.Type = obj.TYPE_REG
   724  	p.To.Reg = REG_R1
   725  
   726  	if framesize <= objabi.StackSmall {
   727  		// small stack: SP < stackguard
   728  		//	CMP	stackguard, SP
   729  		p = obj.Appendp(p, c.newprog)
   730  
   731  		p.As = ACMP
   732  		p.From.Type = obj.TYPE_REG
   733  		p.From.Reg = REG_R1
   734  		p.Reg = REGSP
   735  	} else if framesize <= objabi.StackBig {
   736  		// large stack: SP-framesize < stackguard-StackSmall
   737  		//	MOVW $-(framesize-StackSmall)(SP), R2
   738  		//	CMP stackguard, R2
   739  		p = obj.Appendp(p, c.newprog)
   740  
   741  		p.As = AMOVW
   742  		p.From.Type = obj.TYPE_ADDR
   743  		p.From.Reg = REGSP
   744  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   745  		p.To.Type = obj.TYPE_REG
   746  		p.To.Reg = REG_R2
   747  
   748  		p = obj.Appendp(p, c.newprog)
   749  		p.As = ACMP
   750  		p.From.Type = obj.TYPE_REG
   751  		p.From.Reg = REG_R1
   752  		p.Reg = REG_R2
   753  	} else {
   754  		// Such a large stack we need to protect against wraparound
   755  		// if SP is close to zero.
   756  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   757  		// The +StackGuard on both sides is required to keep the left side positive:
   758  		// SP is allowed to be slightly below stackguard. See stack.h.
   759  		//	CMP     $StackPreempt, R1
   760  		//	MOVW.NE $StackGuard(SP), R2
   761  		//	SUB.NE  R1, R2
   762  		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
   763  		//	CMP.NE  R3, R2
   764  		p = obj.Appendp(p, c.newprog)
   765  
   766  		p.As = ACMP
   767  		p.From.Type = obj.TYPE_CONST
   768  		p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
   769  		p.Reg = REG_R1
   770  
   771  		p = obj.Appendp(p, c.newprog)
   772  		p.As = AMOVW
   773  		p.From.Type = obj.TYPE_ADDR
   774  		p.From.Reg = REGSP
   775  		p.From.Offset = objabi.StackGuard
   776  		p.To.Type = obj.TYPE_REG
   777  		p.To.Reg = REG_R2
   778  		p.Scond = C_SCOND_NE
   779  
   780  		p = obj.Appendp(p, c.newprog)
   781  		p.As = ASUB
   782  		p.From.Type = obj.TYPE_REG
   783  		p.From.Reg = REG_R1
   784  		p.To.Type = obj.TYPE_REG
   785  		p.To.Reg = REG_R2
   786  		p.Scond = C_SCOND_NE
   787  
   788  		p = obj.Appendp(p, c.newprog)
   789  		p.As = AMOVW
   790  		p.From.Type = obj.TYPE_ADDR
   791  		p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
   792  		p.To.Type = obj.TYPE_REG
   793  		p.To.Reg = REG_R3
   794  		p.Scond = C_SCOND_NE
   795  
   796  		p = obj.Appendp(p, c.newprog)
   797  		p.As = ACMP
   798  		p.From.Type = obj.TYPE_REG
   799  		p.From.Reg = REG_R3
   800  		p.Reg = REG_R2
   801  		p.Scond = C_SCOND_NE
   802  	}
   803  
   804  	// BLS call-to-morestack
   805  	bls := obj.Appendp(p, c.newprog)
   806  	bls.As = ABLS
   807  	bls.To.Type = obj.TYPE_BRANCH
   808  
   809  	var last *obj.Prog
   810  	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
   811  	}
   812  
   813  	// Now we are at the end of the function, but logically
   814  	// we are still in function prologue. We need to fix the
   815  	// SP data and PCDATA.
   816  	spfix := obj.Appendp(last, c.newprog)
   817  	spfix.As = obj.ANOP
   818  	spfix.Spadj = -framesize
   819  
   820  	pcdata := obj.Appendp(spfix, c.newprog)
   821  	pcdata.Pos = c.cursym.Func.Text.Pos
   822  	pcdata.As = obj.APCDATA
   823  	pcdata.From.Type = obj.TYPE_CONST
   824  	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
   825  	pcdata.To.Type = obj.TYPE_CONST
   826  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   827  
   828  	// MOVW	LR, R3
   829  	movw := obj.Appendp(pcdata, c.newprog)
   830  	movw.As = AMOVW
   831  	movw.From.Type = obj.TYPE_REG
   832  	movw.From.Reg = REGLINK
   833  	movw.To.Type = obj.TYPE_REG
   834  	movw.To.Reg = REG_R3
   835  
   836  	bls.Pcond = movw
   837  
   838  	// BL runtime.morestack
   839  	call := obj.Appendp(movw, c.newprog)
   840  	call.As = obj.ACALL
   841  	call.To.Type = obj.TYPE_BRANCH
   842  	morestack := "runtime.morestack"
   843  	switch {
   844  	case c.cursym.CFunc():
   845  		morestack = "runtime.morestackc"
   846  	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
   847  		morestack = "runtime.morestack_noctxt"
   848  	}
   849  	call.To.Sym = c.ctxt.Lookup(morestack)
   850  
   851  	// B start
   852  	b := obj.Appendp(call, c.newprog)
   853  	b.As = obj.AJMP
   854  	b.To.Type = obj.TYPE_BRANCH
   855  	b.Pcond = c.cursym.Func.Text.Link
   856  	b.Spadj = +framesize
   857  
   858  	return bls
   859  }
   860  
   861  var unaryDst = map[obj.As]bool{
   862  	ASWI:  true,
   863  	AWORD: true,
   864  }
   865  
   866  var Linkarm = obj.LinkArch{
   867  	Arch:       sys.ArchARM,
   868  	Init:       buildop,
   869  	Preprocess: preprocess,
   870  	Assemble:   span5,
   871  	Progedit:   progedit,
   872  	UnaryDst:   unaryDst,
   873  }