...

Source file src/github.com/twitchyliquid64/golang-asm/obj/arm/obj5.go

Documentation: github.com/twitchyliquid64/golang-asm/obj/arm

     1  // Derived from Inferno utils/5c/swt.c
     2  // https://bitbucket.org/inferno-os/inferno-os/src/master/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  	"github.com/twitchyliquid64/golang-asm/obj"
    35  	"github.com/twitchyliquid64/golang-asm/objabi"
    36  	"github.com/twitchyliquid64/golang-asm/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  	p := c.cursym.Func.Text
   259  	autoffset := int32(p.To.Offset)
   260  	if autoffset == -4 {
   261  		// Historical way to mark NOFRAME.
   262  		p.From.Sym.Set(obj.AttrNoFrame, true)
   263  		autoffset = 0
   264  	}
   265  	if autoffset < 0 || autoffset%4 != 0 {
   266  		c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
   267  	}
   268  	if p.From.Sym.NoFrame() {
   269  		if autoffset != 0 {
   270  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
   271  		}
   272  	}
   273  
   274  	cursym.Func.Locals = autoffset
   275  	cursym.Func.Args = p.To.Val.(int32)
   276  
   277  	/*
   278  	 * find leaf subroutines
   279  	 */
   280  	for p := cursym.Func.Text; p != nil; p = p.Link {
   281  		switch p.As {
   282  		case obj.ATEXT:
   283  			p.Mark |= LEAF
   284  
   285  		case ADIV, ADIVU, AMOD, AMODU:
   286  			cursym.Func.Text.Mark &^= LEAF
   287  
   288  		case ABL,
   289  			ABX,
   290  			obj.ADUFFZERO,
   291  			obj.ADUFFCOPY:
   292  			cursym.Func.Text.Mark &^= LEAF
   293  		}
   294  	}
   295  
   296  	var q2 *obj.Prog
   297  	for p := cursym.Func.Text; p != nil; p = p.Link {
   298  		o := p.As
   299  		switch o {
   300  		case obj.ATEXT:
   301  			autosize = autoffset
   302  
   303  			if p.Mark&LEAF != 0 && autosize == 0 {
   304  				// A leaf function with no locals has no frame.
   305  				p.From.Sym.Set(obj.AttrNoFrame, true)
   306  			}
   307  
   308  			if !p.From.Sym.NoFrame() {
   309  				// If there is a stack frame at all, it includes
   310  				// space to save the LR.
   311  				autosize += 4
   312  			}
   313  
   314  			if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
   315  				// A very few functions that do not return to their caller
   316  				// are not identified as leaves but still have no frame.
   317  				if ctxt.Debugvlog {
   318  					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
   319  				}
   320  
   321  				cursym.Func.Text.Mark |= LEAF
   322  			}
   323  
   324  			// FP offsets need an updated p.To.Offset.
   325  			p.To.Offset = int64(autosize) - 4
   326  
   327  			if cursym.Func.Text.Mark&LEAF != 0 {
   328  				cursym.Set(obj.AttrLeaf, true)
   329  				if p.From.Sym.NoFrame() {
   330  					break
   331  				}
   332  			}
   333  
   334  			if !p.From.Sym.NoSplit() {
   335  				p = c.stacksplit(p, autosize) // emit split check
   336  			}
   337  
   338  			// MOVW.W		R14,$-autosize(SP)
   339  			p = obj.Appendp(p, c.newprog)
   340  
   341  			p.As = AMOVW
   342  			p.Scond |= C_WBIT
   343  			p.From.Type = obj.TYPE_REG
   344  			p.From.Reg = REGLINK
   345  			p.To.Type = obj.TYPE_MEM
   346  			p.To.Offset = int64(-autosize)
   347  			p.To.Reg = REGSP
   348  			p.Spadj = autosize
   349  
   350  			if cursym.Func.Text.From.Sym.Wrapper() {
   351  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   352  				//
   353  				//	MOVW g_panic(g), R1
   354  				//	CMP  $0, R1
   355  				//	B.NE checkargp
   356  				// end:
   357  				//	NOP
   358  				// ... function ...
   359  				// checkargp:
   360  				//	MOVW panic_argp(R1), R2
   361  				//	ADD  $(autosize+4), R13, R3
   362  				//	CMP  R2, R3
   363  				//	B.NE end
   364  				//	ADD  $4, R13, R4
   365  				//	MOVW R4, panic_argp(R1)
   366  				//	B    end
   367  				//
   368  				// The NOP is needed to give the jumps somewhere to land.
   369  				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
   370  
   371  				p = obj.Appendp(p, newprog)
   372  				p.As = AMOVW
   373  				p.From.Type = obj.TYPE_MEM
   374  				p.From.Reg = REGG
   375  				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
   376  				p.To.Type = obj.TYPE_REG
   377  				p.To.Reg = REG_R1
   378  
   379  				p = obj.Appendp(p, newprog)
   380  				p.As = ACMP
   381  				p.From.Type = obj.TYPE_CONST
   382  				p.From.Offset = 0
   383  				p.Reg = REG_R1
   384  
   385  				// B.NE checkargp
   386  				bne := obj.Appendp(p, newprog)
   387  				bne.As = ABNE
   388  				bne.To.Type = obj.TYPE_BRANCH
   389  
   390  				// end: NOP
   391  				end := obj.Appendp(bne, newprog)
   392  				end.As = obj.ANOP
   393  
   394  				// find end of function
   395  				var last *obj.Prog
   396  				for last = end; last.Link != nil; last = last.Link {
   397  				}
   398  
   399  				// MOVW panic_argp(R1), R2
   400  				mov := obj.Appendp(last, newprog)
   401  				mov.As = AMOVW
   402  				mov.From.Type = obj.TYPE_MEM
   403  				mov.From.Reg = REG_R1
   404  				mov.From.Offset = 0 // Panic.argp
   405  				mov.To.Type = obj.TYPE_REG
   406  				mov.To.Reg = REG_R2
   407  
   408  				// B.NE branch target is MOVW above
   409  				bne.To.SetTarget(mov)
   410  
   411  				// ADD $(autosize+4), R13, R3
   412  				p = obj.Appendp(mov, newprog)
   413  				p.As = AADD
   414  				p.From.Type = obj.TYPE_CONST
   415  				p.From.Offset = int64(autosize) + 4
   416  				p.Reg = REG_R13
   417  				p.To.Type = obj.TYPE_REG
   418  				p.To.Reg = REG_R3
   419  
   420  				// CMP R2, R3
   421  				p = obj.Appendp(p, newprog)
   422  				p.As = ACMP
   423  				p.From.Type = obj.TYPE_REG
   424  				p.From.Reg = REG_R2
   425  				p.Reg = REG_R3
   426  
   427  				// B.NE end
   428  				p = obj.Appendp(p, newprog)
   429  				p.As = ABNE
   430  				p.To.Type = obj.TYPE_BRANCH
   431  				p.To.SetTarget(end)
   432  
   433  				// ADD $4, R13, R4
   434  				p = obj.Appendp(p, newprog)
   435  				p.As = AADD
   436  				p.From.Type = obj.TYPE_CONST
   437  				p.From.Offset = 4
   438  				p.Reg = REG_R13
   439  				p.To.Type = obj.TYPE_REG
   440  				p.To.Reg = REG_R4
   441  
   442  				// MOVW R4, panic_argp(R1)
   443  				p = obj.Appendp(p, newprog)
   444  				p.As = AMOVW
   445  				p.From.Type = obj.TYPE_REG
   446  				p.From.Reg = REG_R4
   447  				p.To.Type = obj.TYPE_MEM
   448  				p.To.Reg = REG_R1
   449  				p.To.Offset = 0 // Panic.argp
   450  
   451  				// B end
   452  				p = obj.Appendp(p, newprog)
   453  				p.As = AB
   454  				p.To.Type = obj.TYPE_BRANCH
   455  				p.To.SetTarget(end)
   456  
   457  				// reset for subsequent passes
   458  				p = end
   459  			}
   460  
   461  		case obj.ARET:
   462  			nocache(p)
   463  			if cursym.Func.Text.Mark&LEAF != 0 {
   464  				if autosize == 0 {
   465  					p.As = AB
   466  					p.From = obj.Addr{}
   467  					if p.To.Sym != nil { // retjmp
   468  						p.To.Type = obj.TYPE_BRANCH
   469  					} else {
   470  						p.To.Type = obj.TYPE_MEM
   471  						p.To.Offset = 0
   472  						p.To.Reg = REGLINK
   473  					}
   474  
   475  					break
   476  				}
   477  			}
   478  
   479  			p.As = AMOVW
   480  			p.Scond |= C_PBIT
   481  			p.From.Type = obj.TYPE_MEM
   482  			p.From.Offset = int64(autosize)
   483  			p.From.Reg = REGSP
   484  			p.To.Type = obj.TYPE_REG
   485  			p.To.Reg = REGPC
   486  
   487  			// If there are instructions following
   488  			// this ARET, they come from a branch
   489  			// with the same stackframe, so no spadj.
   490  			if p.To.Sym != nil { // retjmp
   491  				p.To.Reg = REGLINK
   492  				q2 = obj.Appendp(p, newprog)
   493  				q2.As = AB
   494  				q2.To.Type = obj.TYPE_BRANCH
   495  				q2.To.Sym = p.To.Sym
   496  				p.To.Sym = nil
   497  				p = q2
   498  			}
   499  
   500  		case AADD:
   501  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   502  				p.Spadj = int32(-p.From.Offset)
   503  			}
   504  
   505  		case ASUB:
   506  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   507  				p.Spadj = int32(p.From.Offset)
   508  			}
   509  
   510  		case ADIV, ADIVU, AMOD, AMODU:
   511  			if cursym.Func.Text.From.Sym.NoSplit() {
   512  				ctxt.Diag("cannot divide in NOSPLIT function")
   513  			}
   514  			const debugdivmod = false
   515  			if debugdivmod {
   516  				break
   517  			}
   518  			if p.From.Type != obj.TYPE_REG {
   519  				break
   520  			}
   521  			if p.To.Type != obj.TYPE_REG {
   522  				break
   523  			}
   524  
   525  			// Make copy because we overwrite p below.
   526  			q1 := *p
   527  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
   528  				ctxt.Diag("div already using REGTMP: %v", p)
   529  			}
   530  
   531  			/* MOV m(g),REGTMP */
   532  			p.As = AMOVW
   533  			p.Pos = q1.Pos
   534  			p.From.Type = obj.TYPE_MEM
   535  			p.From.Reg = REGG
   536  			p.From.Offset = 6 * 4 // offset of g.m
   537  			p.Reg = 0
   538  			p.To.Type = obj.TYPE_REG
   539  			p.To.Reg = REGTMP
   540  
   541  			/* MOV a,m_divmod(REGTMP) */
   542  			p = obj.Appendp(p, newprog)
   543  			p.As = AMOVW
   544  			p.Pos = q1.Pos
   545  			p.From.Type = obj.TYPE_REG
   546  			p.From.Reg = q1.From.Reg
   547  			p.To.Type = obj.TYPE_MEM
   548  			p.To.Reg = REGTMP
   549  			p.To.Offset = 8 * 4 // offset of m.divmod
   550  
   551  			/* MOV b, R8 */
   552  			p = obj.Appendp(p, newprog)
   553  			p.As = AMOVW
   554  			p.Pos = q1.Pos
   555  			p.From.Type = obj.TYPE_REG
   556  			p.From.Reg = q1.Reg
   557  			if q1.Reg == 0 {
   558  				p.From.Reg = q1.To.Reg
   559  			}
   560  			p.To.Type = obj.TYPE_REG
   561  			p.To.Reg = REG_R8
   562  			p.To.Offset = 0
   563  
   564  			/* CALL appropriate */
   565  			p = obj.Appendp(p, newprog)
   566  			p.As = ABL
   567  			p.Pos = q1.Pos
   568  			p.To.Type = obj.TYPE_BRANCH
   569  			switch o {
   570  			case ADIV:
   571  				p.To.Sym = symdiv
   572  			case ADIVU:
   573  				p.To.Sym = symdivu
   574  			case AMOD:
   575  				p.To.Sym = symmod
   576  			case AMODU:
   577  				p.To.Sym = symmodu
   578  			}
   579  
   580  			/* MOV REGTMP, b */
   581  			p = obj.Appendp(p, newprog)
   582  			p.As = AMOVW
   583  			p.Pos = q1.Pos
   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  		case AMOVW:
   591  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
   592  				p.Spadj = int32(-p.To.Offset)
   593  			}
   594  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
   595  				p.Spadj = int32(-p.From.Offset)
   596  			}
   597  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
   598  				p.Spadj = int32(-p.From.Offset)
   599  			}
   600  
   601  		case obj.AGETCALLERPC:
   602  			if cursym.Leaf() {
   603  				/* MOVW LR, Rd */
   604  				p.As = AMOVW
   605  				p.From.Type = obj.TYPE_REG
   606  				p.From.Reg = REGLINK
   607  			} else {
   608  				/* MOVW (RSP), Rd */
   609  				p.As = AMOVW
   610  				p.From.Type = obj.TYPE_MEM
   611  				p.From.Reg = REGSP
   612  			}
   613  		}
   614  	}
   615  }
   616  
   617  func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   618  	// MOVW g_stackguard(g), R1
   619  	p = obj.Appendp(p, c.newprog)
   620  
   621  	p.As = AMOVW
   622  	p.From.Type = obj.TYPE_MEM
   623  	p.From.Reg = REGG
   624  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   625  	if c.cursym.CFunc() {
   626  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   627  	}
   628  	p.To.Type = obj.TYPE_REG
   629  	p.To.Reg = REG_R1
   630  
   631  	// Mark the stack bound check and morestack call async nonpreemptible.
   632  	// If we get preempted here, when resumed the preemption request is
   633  	// cleared, but we'll still call morestack, which will double the stack
   634  	// unnecessarily. See issue #35470.
   635  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   636  
   637  	if framesize <= objabi.StackSmall {
   638  		// small stack: SP < stackguard
   639  		//	CMP	stackguard, SP
   640  		p = obj.Appendp(p, c.newprog)
   641  
   642  		p.As = ACMP
   643  		p.From.Type = obj.TYPE_REG
   644  		p.From.Reg = REG_R1
   645  		p.Reg = REGSP
   646  	} else if framesize <= objabi.StackBig {
   647  		// large stack: SP-framesize < stackguard-StackSmall
   648  		//	MOVW $-(framesize-StackSmall)(SP), R2
   649  		//	CMP stackguard, R2
   650  		p = obj.Appendp(p, c.newprog)
   651  
   652  		p.As = AMOVW
   653  		p.From.Type = obj.TYPE_ADDR
   654  		p.From.Reg = REGSP
   655  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   656  		p.To.Type = obj.TYPE_REG
   657  		p.To.Reg = REG_R2
   658  
   659  		p = obj.Appendp(p, c.newprog)
   660  		p.As = ACMP
   661  		p.From.Type = obj.TYPE_REG
   662  		p.From.Reg = REG_R1
   663  		p.Reg = REG_R2
   664  	} else {
   665  		// Such a large stack we need to protect against wraparound
   666  		// if SP is close to zero.
   667  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   668  		// The +StackGuard on both sides is required to keep the left side positive:
   669  		// SP is allowed to be slightly below stackguard. See stack.h.
   670  		//	CMP     $StackPreempt, R1
   671  		//	MOVW.NE $StackGuard(SP), R2
   672  		//	SUB.NE  R1, R2
   673  		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
   674  		//	CMP.NE  R3, R2
   675  		p = obj.Appendp(p, c.newprog)
   676  
   677  		p.As = ACMP
   678  		p.From.Type = obj.TYPE_CONST
   679  		p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
   680  		p.Reg = REG_R1
   681  
   682  		p = obj.Appendp(p, c.newprog)
   683  		p.As = AMOVW
   684  		p.From.Type = obj.TYPE_ADDR
   685  		p.From.Reg = REGSP
   686  		p.From.Offset = int64(objabi.StackGuard)
   687  		p.To.Type = obj.TYPE_REG
   688  		p.To.Reg = REG_R2
   689  		p.Scond = C_SCOND_NE
   690  
   691  		p = obj.Appendp(p, c.newprog)
   692  		p.As = ASUB
   693  		p.From.Type = obj.TYPE_REG
   694  		p.From.Reg = REG_R1
   695  		p.To.Type = obj.TYPE_REG
   696  		p.To.Reg = REG_R2
   697  		p.Scond = C_SCOND_NE
   698  
   699  		p = obj.Appendp(p, c.newprog)
   700  		p.As = AMOVW
   701  		p.From.Type = obj.TYPE_ADDR
   702  		p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
   703  		p.To.Type = obj.TYPE_REG
   704  		p.To.Reg = REG_R3
   705  		p.Scond = C_SCOND_NE
   706  
   707  		p = obj.Appendp(p, c.newprog)
   708  		p.As = ACMP
   709  		p.From.Type = obj.TYPE_REG
   710  		p.From.Reg = REG_R3
   711  		p.Reg = REG_R2
   712  		p.Scond = C_SCOND_NE
   713  	}
   714  
   715  	// BLS call-to-morestack
   716  	bls := obj.Appendp(p, c.newprog)
   717  	bls.As = ABLS
   718  	bls.To.Type = obj.TYPE_BRANCH
   719  
   720  	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
   721  
   722  	var last *obj.Prog
   723  	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
   724  	}
   725  
   726  	// Now we are at the end of the function, but logically
   727  	// we are still in function prologue. We need to fix the
   728  	// SP data and PCDATA.
   729  	spfix := obj.Appendp(last, c.newprog)
   730  	spfix.As = obj.ANOP
   731  	spfix.Spadj = -framesize
   732  
   733  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
   734  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
   735  
   736  	// MOVW	LR, R3
   737  	movw := obj.Appendp(pcdata, c.newprog)
   738  	movw.As = AMOVW
   739  	movw.From.Type = obj.TYPE_REG
   740  	movw.From.Reg = REGLINK
   741  	movw.To.Type = obj.TYPE_REG
   742  	movw.To.Reg = REG_R3
   743  
   744  	bls.To.SetTarget(movw)
   745  
   746  	// BL runtime.morestack
   747  	call := obj.Appendp(movw, c.newprog)
   748  	call.As = obj.ACALL
   749  	call.To.Type = obj.TYPE_BRANCH
   750  	morestack := "runtime.morestack"
   751  	switch {
   752  	case c.cursym.CFunc():
   753  		morestack = "runtime.morestackc"
   754  	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
   755  		morestack = "runtime.morestack_noctxt"
   756  	}
   757  	call.To.Sym = c.ctxt.Lookup(morestack)
   758  
   759  	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
   760  
   761  	// B start
   762  	b := obj.Appendp(pcdata, c.newprog)
   763  	b.As = obj.AJMP
   764  	b.To.Type = obj.TYPE_BRANCH
   765  	b.To.SetTarget(c.cursym.Func.Text.Link)
   766  	b.Spadj = +framesize
   767  
   768  	return end
   769  }
   770  
   771  var unaryDst = map[obj.As]bool{
   772  	ASWI:  true,
   773  	AWORD: true,
   774  }
   775  
   776  var Linkarm = obj.LinkArch{
   777  	Arch:           sys.ArchARM,
   778  	Init:           buildop,
   779  	Preprocess:     preprocess,
   780  	Assemble:       span5,
   781  	Progedit:       progedit,
   782  	UnaryDst:       unaryDst,
   783  	DWARFRegisters: ARMDWARFRegisters,
   784  }
   785  

View as plain text