github.com/aloncn/graphics-go@v0.0.1/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_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 o int
   346  	var p1 *obj.Prog
   347  	var p2 *obj.Prog
   348  	var q2 *obj.Prog
   349  	for p := cursym.Text; p != nil; p = p.Link {
   350  		o = int(p.As)
   351  		switch o {
   352  		case obj.ATEXT:
   353  			autosize = int32(p.To.Offset + 4)
   354  			if autosize <= 4 {
   355  				if cursym.Text.Mark&LEAF != 0 {
   356  					p.To.Offset = -4
   357  					autosize = 0
   358  				}
   359  			}
   360  
   361  			if autosize == 0 && cursym.Text.Mark&LEAF == 0 {
   362  				if ctxt.Debugvlog != 0 {
   363  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Name)
   364  					ctxt.Bso.Flush()
   365  				}
   366  
   367  				cursym.Text.Mark |= LEAF
   368  			}
   369  
   370  			if cursym.Text.Mark&LEAF != 0 {
   371  				cursym.Leaf = 1
   372  				if autosize == 0 {
   373  					break
   374  				}
   375  			}
   376  
   377  			if p.From3.Offset&obj.NOSPLIT == 0 {
   378  				p = stacksplit(ctxt, p, autosize) // emit split check
   379  			}
   380  
   381  			// MOVW.W		R14,$-autosize(SP)
   382  			p = obj.Appendp(ctxt, p)
   383  
   384  			p.As = AMOVW
   385  			p.Scond |= C_WBIT
   386  			p.From.Type = obj.TYPE_REG
   387  			p.From.Reg = REGLINK
   388  			p.To.Type = obj.TYPE_MEM
   389  			p.To.Offset = int64(-autosize)
   390  			p.To.Reg = REGSP
   391  			p.Spadj = autosize
   392  
   393  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   394  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   395  				//
   396  				//	MOVW g_panic(g), R1
   397  				//	CMP $0, R1
   398  				//	B.EQ end
   399  				//	MOVW panic_argp(R1), R2
   400  				//	ADD $(autosize+4), R13, R3
   401  				//	CMP R2, R3
   402  				//	B.NE end
   403  				//	ADD $4, R13, R4
   404  				//	MOVW R4, panic_argp(R1)
   405  				// end:
   406  				//	NOP
   407  				//
   408  				// The NOP is needed to give the jumps somewhere to land.
   409  				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
   410  
   411  				p = obj.Appendp(ctxt, p)
   412  
   413  				p.As = AMOVW
   414  				p.From.Type = obj.TYPE_MEM
   415  				p.From.Reg = REGG
   416  				p.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   417  				p.To.Type = obj.TYPE_REG
   418  				p.To.Reg = REG_R1
   419  
   420  				p = obj.Appendp(ctxt, p)
   421  				p.As = ACMP
   422  				p.From.Type = obj.TYPE_CONST
   423  				p.From.Offset = 0
   424  				p.Reg = REG_R1
   425  
   426  				p = obj.Appendp(ctxt, p)
   427  				p.As = ABEQ
   428  				p.To.Type = obj.TYPE_BRANCH
   429  				p1 = p
   430  
   431  				p = obj.Appendp(ctxt, p)
   432  				p.As = AMOVW
   433  				p.From.Type = obj.TYPE_MEM
   434  				p.From.Reg = REG_R1
   435  				p.From.Offset = 0 // Panic.argp
   436  				p.To.Type = obj.TYPE_REG
   437  				p.To.Reg = REG_R2
   438  
   439  				p = obj.Appendp(ctxt, p)
   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  				p = obj.Appendp(ctxt, p)
   448  				p.As = ACMP
   449  				p.From.Type = obj.TYPE_REG
   450  				p.From.Reg = REG_R2
   451  				p.Reg = REG_R3
   452  
   453  				p = obj.Appendp(ctxt, p)
   454  				p.As = ABNE
   455  				p.To.Type = obj.TYPE_BRANCH
   456  				p2 = p
   457  
   458  				p = obj.Appendp(ctxt, p)
   459  				p.As = AADD
   460  				p.From.Type = obj.TYPE_CONST
   461  				p.From.Offset = 4
   462  				p.Reg = REG_R13
   463  				p.To.Type = obj.TYPE_REG
   464  				p.To.Reg = REG_R4
   465  
   466  				p = obj.Appendp(ctxt, p)
   467  				p.As = AMOVW
   468  				p.From.Type = obj.TYPE_REG
   469  				p.From.Reg = REG_R4
   470  				p.To.Type = obj.TYPE_MEM
   471  				p.To.Reg = REG_R1
   472  				p.To.Offset = 0 // Panic.argp
   473  
   474  				p = obj.Appendp(ctxt, p)
   475  
   476  				p.As = obj.ANOP
   477  				p1.Pcond = p
   478  				p2.Pcond = p
   479  			}
   480  
   481  		case obj.ARET:
   482  			obj.Nocache(p)
   483  			if cursym.Text.Mark&LEAF != 0 {
   484  				if autosize == 0 {
   485  					p.As = AB
   486  					p.From = obj.Addr{}
   487  					if p.To.Sym != nil { // retjmp
   488  						p.To.Type = obj.TYPE_BRANCH
   489  					} else {
   490  						p.To.Type = obj.TYPE_MEM
   491  						p.To.Offset = 0
   492  						p.To.Reg = REGLINK
   493  					}
   494  
   495  					break
   496  				}
   497  			}
   498  
   499  			p.As = AMOVW
   500  			p.Scond |= C_PBIT
   501  			p.From.Type = obj.TYPE_MEM
   502  			p.From.Offset = int64(autosize)
   503  			p.From.Reg = REGSP
   504  			p.To.Type = obj.TYPE_REG
   505  			p.To.Reg = REGPC
   506  
   507  			// If there are instructions following
   508  			// this ARET, they come from a branch
   509  			// with the same stackframe, so no spadj.
   510  			if p.To.Sym != nil { // retjmp
   511  				p.To.Reg = REGLINK
   512  				q2 = obj.Appendp(ctxt, p)
   513  				q2.As = AB
   514  				q2.To.Type = obj.TYPE_BRANCH
   515  				q2.To.Sym = p.To.Sym
   516  				p.To.Sym = nil
   517  				p = q2
   518  			}
   519  
   520  		case AADD:
   521  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   522  				p.Spadj = int32(-p.From.Offset)
   523  			}
   524  
   525  		case ASUB:
   526  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   527  				p.Spadj = int32(p.From.Offset)
   528  			}
   529  
   530  		case ADIV, ADIVU, AMOD, AMODU:
   531  			if cursym.Text.From3.Offset&obj.NOSPLIT != 0 {
   532  				ctxt.Diag("cannot divide in NOSPLIT function")
   533  			}
   534  			if ctxt.Debugdivmod != 0 {
   535  				break
   536  			}
   537  			if p.From.Type != obj.TYPE_REG {
   538  				break
   539  			}
   540  			if p.To.Type != obj.TYPE_REG {
   541  				break
   542  			}
   543  
   544  			// Make copy because we overwrite p below.
   545  			q1 := *p
   546  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   547  				ctxt.Diag("div already using REGTMP: %v", p)
   548  			}
   549  
   550  			/* MOV m(g),REGTMP */
   551  			p.As = AMOVW
   552  			p.Lineno = q1.Lineno
   553  			p.From.Type = obj.TYPE_MEM
   554  			p.From.Reg = REGG
   555  			p.From.Offset = 6 * 4 // offset of g.m
   556  			p.Reg = 0
   557  			p.To.Type = obj.TYPE_REG
   558  			p.To.Reg = REGTMP
   559  
   560  			/* MOV a,m_divmod(REGTMP) */
   561  			p = obj.Appendp(ctxt, p)
   562  			p.As = AMOVW
   563  			p.Lineno = q1.Lineno
   564  			p.From.Type = obj.TYPE_REG
   565  			p.From.Reg = q1.From.Reg
   566  			p.To.Type = obj.TYPE_MEM
   567  			p.To.Reg = REGTMP
   568  			p.To.Offset = 8 * 4 // offset of m.divmod
   569  
   570  			/* MOV b,REGTMP */
   571  			p = obj.Appendp(ctxt, p)
   572  			p.As = AMOVW
   573  			p.Lineno = q1.Lineno
   574  			p.From.Type = obj.TYPE_REG
   575  			p.From.Reg = q1.Reg
   576  			if q1.Reg == 0 {
   577  				p.From.Reg = q1.To.Reg
   578  			}
   579  			p.To.Type = obj.TYPE_REG
   580  			p.To.Reg = REGTMP
   581  			p.To.Offset = 0
   582  
   583  			/* CALL appropriate */
   584  			p = obj.Appendp(ctxt, p)
   585  			p.As = ABL
   586  			p.Lineno = q1.Lineno
   587  			p.To.Type = obj.TYPE_BRANCH
   588  			switch o {
   589  			case ADIV:
   590  				p.To.Sym = ctxt.Sym_div
   591  
   592  			case ADIVU:
   593  				p.To.Sym = ctxt.Sym_divu
   594  
   595  			case AMOD:
   596  				p.To.Sym = ctxt.Sym_mod
   597  
   598  			case AMODU:
   599  				p.To.Sym = ctxt.Sym_modu
   600  			}
   601  
   602  			/* MOV REGTMP, b */
   603  			p = obj.Appendp(ctxt, p)
   604  			p.As = AMOVW
   605  			p.Lineno = q1.Lineno
   606  			p.From.Type = obj.TYPE_REG
   607  			p.From.Reg = REGTMP
   608  			p.From.Offset = 0
   609  			p.To.Type = obj.TYPE_REG
   610  			p.To.Reg = q1.To.Reg
   611  
   612  		case AMOVW:
   613  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   614  				p.Spadj = int32(-p.To.Offset)
   615  			}
   616  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   617  				p.Spadj = int32(-p.From.Offset)
   618  			}
   619  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   620  				p.Spadj = int32(-p.From.Offset)
   621  			}
   622  		}
   623  	}
   624  }
   625  
   626  func isfloatreg(a *obj.Addr) bool {
   627  	return a.Type == obj.TYPE_REG && REG_F0 <= a.Reg && a.Reg <= REG_F15
   628  }
   629  
   630  func softfloat(ctxt *obj.Link, cursym *obj.LSym) {
   631  	if ctxt.Goarm > 5 {
   632  		return
   633  	}
   634  
   635  	symsfloat := obj.Linklookup(ctxt, "_sfloat", 0)
   636  
   637  	wasfloat := 0
   638  	for p := cursym.Text; p != nil; p = p.Link {
   639  		if p.Pcond != nil {
   640  			p.Pcond.Mark |= LABEL
   641  		}
   642  	}
   643  	var next *obj.Prog
   644  	for p := cursym.Text; p != nil; p = p.Link {
   645  		switch p.As {
   646  		case AMOVW:
   647  			if isfloatreg(&p.To) || isfloatreg(&p.From) {
   648  				goto soft
   649  			}
   650  			goto notsoft
   651  
   652  		case AMOVWD,
   653  			AMOVWF,
   654  			AMOVDW,
   655  			AMOVFW,
   656  			AMOVFD,
   657  			AMOVDF,
   658  			AMOVF,
   659  			AMOVD,
   660  			ACMPF,
   661  			ACMPD,
   662  			AADDF,
   663  			AADDD,
   664  			ASUBF,
   665  			ASUBD,
   666  			AMULF,
   667  			AMULD,
   668  			ADIVF,
   669  			ADIVD,
   670  			ASQRTF,
   671  			ASQRTD,
   672  			AABSF,
   673  			AABSD:
   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 != 0 {
   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  	spfix := obj.Appendp(ctxt, last)
   807  	spfix.As = obj.ANOP
   808  	spfix.Spadj = -framesize
   809  
   810  	// MOVW	LR, R3
   811  	movw := obj.Appendp(ctxt, spfix)
   812  	movw.As = AMOVW
   813  	movw.From.Type = obj.TYPE_REG
   814  	movw.From.Reg = REGLINK
   815  	movw.To.Type = obj.TYPE_REG
   816  	movw.To.Reg = REG_R3
   817  
   818  	bls.Pcond = movw
   819  
   820  	// BL runtime.morestack
   821  	call := obj.Appendp(ctxt, movw)
   822  	call.As = obj.ACALL
   823  	call.To.Type = obj.TYPE_BRANCH
   824  	morestack := "runtime.morestack"
   825  	switch {
   826  	case ctxt.Cursym.Cfunc != 0:
   827  		morestack = "runtime.morestackc"
   828  	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
   829  		morestack = "runtime.morestack_noctxt"
   830  	}
   831  	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
   832  
   833  	// B start
   834  	b := obj.Appendp(ctxt, call)
   835  	b.As = obj.AJMP
   836  	b.To.Type = obj.TYPE_BRANCH
   837  	b.Pcond = ctxt.Cursym.Text.Link
   838  	b.Spadj = +framesize
   839  
   840  	return bls
   841  }
   842  
   843  func initdiv(ctxt *obj.Link) {
   844  	if ctxt.Sym_div != nil {
   845  		return
   846  	}
   847  	ctxt.Sym_div = obj.Linklookup(ctxt, "_div", 0)
   848  	ctxt.Sym_divu = obj.Linklookup(ctxt, "_divu", 0)
   849  	ctxt.Sym_mod = obj.Linklookup(ctxt, "_mod", 0)
   850  	ctxt.Sym_modu = obj.Linklookup(ctxt, "_modu", 0)
   851  }
   852  
   853  func follow(ctxt *obj.Link, s *obj.LSym) {
   854  	ctxt.Cursym = s
   855  
   856  	firstp := ctxt.NewProg()
   857  	lastp := firstp
   858  	xfol(ctxt, s.Text, &lastp)
   859  	lastp.Link = nil
   860  	s.Text = firstp.Link
   861  }
   862  
   863  func relinv(a int) int {
   864  	switch a {
   865  	case ABEQ:
   866  		return ABNE
   867  	case ABNE:
   868  		return ABEQ
   869  	case ABCS:
   870  		return ABCC
   871  	case ABHS:
   872  		return ABLO
   873  	case ABCC:
   874  		return ABCS
   875  	case ABLO:
   876  		return ABHS
   877  	case ABMI:
   878  		return ABPL
   879  	case ABPL:
   880  		return ABMI
   881  	case ABVS:
   882  		return ABVC
   883  	case ABVC:
   884  		return ABVS
   885  	case ABHI:
   886  		return ABLS
   887  	case ABLS:
   888  		return ABHI
   889  	case ABGE:
   890  		return ABLT
   891  	case ABLT:
   892  		return ABGE
   893  	case ABGT:
   894  		return ABLE
   895  	case ABLE:
   896  		return ABGT
   897  	}
   898  
   899  	log.Fatalf("unknown relation: %s", Anames[a])
   900  	return 0
   901  }
   902  
   903  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   904  	var q *obj.Prog
   905  	var r *obj.Prog
   906  	var a int
   907  	var i int
   908  
   909  loop:
   910  	if p == nil {
   911  		return
   912  	}
   913  	a = int(p.As)
   914  	if a == AB {
   915  		q = p.Pcond
   916  		if q != nil && q.As != obj.ATEXT {
   917  			p.Mark |= FOLL
   918  			p = q
   919  			if p.Mark&FOLL == 0 {
   920  				goto loop
   921  			}
   922  		}
   923  	}
   924  
   925  	if p.Mark&FOLL != 0 {
   926  		i = 0
   927  		q = p
   928  		for ; i < 4; i, q = i+1, q.Link {
   929  			if q == *last || q == nil {
   930  				break
   931  			}
   932  			a = int(q.As)
   933  			if a == obj.ANOP {
   934  				i--
   935  				continue
   936  			}
   937  
   938  			if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   939  				goto copy
   940  			}
   941  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   942  				continue
   943  			}
   944  			if a != ABEQ && a != ABNE {
   945  				continue
   946  			}
   947  
   948  		copy:
   949  			for {
   950  				r = ctxt.NewProg()
   951  				*r = *p
   952  				if r.Mark&FOLL == 0 {
   953  					fmt.Printf("can't happen 1\n")
   954  				}
   955  				r.Mark |= FOLL
   956  				if p != q {
   957  					p = p.Link
   958  					(*last).Link = r
   959  					*last = r
   960  					continue
   961  				}
   962  
   963  				(*last).Link = r
   964  				*last = r
   965  				if a == AB || (a == obj.ARET && q.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   966  					return
   967  				}
   968  				r.As = ABNE
   969  				if a == ABNE {
   970  					r.As = ABEQ
   971  				}
   972  				r.Pcond = p.Link
   973  				r.Link = p.Pcond
   974  				if r.Link.Mark&FOLL == 0 {
   975  					xfol(ctxt, r.Link, last)
   976  				}
   977  				if r.Pcond.Mark&FOLL == 0 {
   978  					fmt.Printf("can't happen 2\n")
   979  				}
   980  				return
   981  			}
   982  		}
   983  
   984  		a = AB
   985  		q = ctxt.NewProg()
   986  		q.As = int16(a)
   987  		q.Lineno = p.Lineno
   988  		q.To.Type = obj.TYPE_BRANCH
   989  		q.To.Offset = p.Pc
   990  		q.Pcond = p
   991  		p = q
   992  	}
   993  
   994  	p.Mark |= FOLL
   995  	(*last).Link = p
   996  	*last = p
   997  	if a == AB || (a == obj.ARET && p.Scond == C_SCOND_NONE) || a == ARFE || a == obj.AUNDEF {
   998  		return
   999  	}
  1000  
  1001  	if p.Pcond != nil {
  1002  		if a != ABL && a != ABX && p.Link != nil {
  1003  			q = obj.Brchain(ctxt, p.Link)
  1004  			if a != obj.ATEXT {
  1005  				if q != nil && (q.Mark&FOLL != 0) {
  1006  					p.As = int16(relinv(a))
  1007  					p.Link = p.Pcond
  1008  					p.Pcond = q
  1009  				}
  1010  			}
  1011  
  1012  			xfol(ctxt, p.Link, last)
  1013  			q = obj.Brchain(ctxt, p.Pcond)
  1014  			if q == nil {
  1015  				q = p.Pcond
  1016  			}
  1017  			if q.Mark&FOLL != 0 {
  1018  				p.Pcond = q
  1019  				return
  1020  			}
  1021  
  1022  			p = q
  1023  			goto loop
  1024  		}
  1025  	}
  1026  
  1027  	p = p.Link
  1028  	goto loop
  1029  }
  1030  
  1031  var unaryDst = map[int]bool{
  1032  	ASWI:  true,
  1033  	AWORD: true,
  1034  }
  1035  
  1036  var Linkarm = obj.LinkArch{
  1037  	ByteOrder:  binary.LittleEndian,
  1038  	Name:       "arm",
  1039  	Thechar:    '5',
  1040  	Preprocess: preprocess,
  1041  	Assemble:   span5,
  1042  	Follow:     follow,
  1043  	Progedit:   progedit,
  1044  	UnaryDst:   unaryDst,
  1045  	Minlc:      4,
  1046  	Ptrsize:    4,
  1047  	Regsize:    4,
  1048  }