Source file src/cmd/internal/obj/arm64/obj7.go

     1  // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
     2  // https://bitbucket.org/plan9-from-bell-labs/9-cc/src/master/
     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 arm64
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"cmd/internal/objabi"
    36  	"cmd/internal/src"
    37  	"cmd/internal/sys"
    38  	"internal/abi"
    39  	"internal/buildcfg"
    40  	"log"
    41  	"math"
    42  )
    43  
    44  // zrReplace is the set of instructions for which $0 in the From operand
    45  // should be replaced with REGZERO.
    46  var zrReplace = map[obj.As]bool{
    47  	AMOVD:  true,
    48  	AMOVW:  true,
    49  	AMOVWU: true,
    50  	AMOVH:  true,
    51  	AMOVHU: true,
    52  	AMOVB:  true,
    53  	AMOVBU: true,
    54  	ASBC:   true,
    55  	ASBCW:  true,
    56  	ASBCS:  true,
    57  	ASBCSW: true,
    58  	AADC:   true,
    59  	AADCW:  true,
    60  	AADCS:  true,
    61  	AADCSW: true,
    62  	AFMOVD: true,
    63  	AFMOVS: true,
    64  	AMSR:   true,
    65  }
    66  
    67  func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
    68  	if c.ctxt.Flag_maymorestack != "" {
    69  		p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
    70  
    71  		// Save LR and make room for FP, REGCTXT. Leave room
    72  		// for caller's saved FP.
    73  		const frameSize = 32
    74  		p = obj.Appendp(p, c.newprog)
    75  		p.As = AMOVD
    76  		p.From.Type = obj.TYPE_REG
    77  		p.From.Reg = REGLINK
    78  		p.To.Type = obj.TYPE_MEM
    79  		p.Scond = C_XPRE
    80  		p.To.Offset = -frameSize
    81  		p.To.Reg = REGSP
    82  		p.Spadj = frameSize
    83  
    84  		// Save FP.
    85  		p = obj.Appendp(p, c.newprog)
    86  		p.As = AMOVD
    87  		p.From.Type = obj.TYPE_REG
    88  		p.From.Reg = REGFP
    89  		p.To.Type = obj.TYPE_MEM
    90  		p.To.Reg = REGSP
    91  		p.To.Offset = -8
    92  
    93  		p = obj.Appendp(p, c.newprog)
    94  		p.As = ASUB
    95  		p.From.Type = obj.TYPE_CONST
    96  		p.From.Offset = 8
    97  		p.Reg = REGSP
    98  		p.To.Type = obj.TYPE_REG
    99  		p.To.Reg = REGFP
   100  
   101  		// Save REGCTXT (for simplicity we do this whether or
   102  		// not we need it.)
   103  		p = obj.Appendp(p, c.newprog)
   104  		p.As = AMOVD
   105  		p.From.Type = obj.TYPE_REG
   106  		p.From.Reg = REGCTXT
   107  		p.To.Type = obj.TYPE_MEM
   108  		p.To.Reg = REGSP
   109  		p.To.Offset = 8
   110  
   111  		// BL maymorestack
   112  		p = obj.Appendp(p, c.newprog)
   113  		p.As = ABL
   114  		p.To.Type = obj.TYPE_BRANCH
   115  		// See ../x86/obj6.go
   116  		p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
   117  
   118  		// Restore REGCTXT.
   119  		p = obj.Appendp(p, c.newprog)
   120  		p.As = AMOVD
   121  		p.From.Type = obj.TYPE_MEM
   122  		p.From.Reg = REGSP
   123  		p.From.Offset = 8
   124  		p.To.Type = obj.TYPE_REG
   125  		p.To.Reg = REGCTXT
   126  
   127  		// Restore FP.
   128  		p = obj.Appendp(p, c.newprog)
   129  		p.As = AMOVD
   130  		p.From.Type = obj.TYPE_MEM
   131  		p.From.Reg = REGSP
   132  		p.From.Offset = -8
   133  		p.To.Type = obj.TYPE_REG
   134  		p.To.Reg = REGFP
   135  
   136  		// Restore LR and SP.
   137  		p = obj.Appendp(p, c.newprog)
   138  		p.As = AMOVD
   139  		p.From.Type = obj.TYPE_MEM
   140  		p.Scond = C_XPOST
   141  		p.From.Offset = frameSize
   142  		p.From.Reg = REGSP
   143  		p.To.Type = obj.TYPE_REG
   144  		p.To.Reg = REGLINK
   145  		p.Spadj = -frameSize
   146  
   147  		p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
   148  	}
   149  
   150  	// Jump back to here after morestack returns.
   151  	startPred := p
   152  
   153  	// MOV	g_stackguard(g), RT1
   154  	p = obj.Appendp(p, c.newprog)
   155  
   156  	p.As = AMOVD
   157  	p.From.Type = obj.TYPE_MEM
   158  	p.From.Reg = REGG
   159  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   160  	if c.cursym.CFunc() {
   161  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   162  	}
   163  	p.To.Type = obj.TYPE_REG
   164  	p.To.Reg = REGRT1
   165  
   166  	// Mark the stack bound check and morestack call async nonpreemptible.
   167  	// If we get preempted here, when resumed the preemption request is
   168  	// cleared, but we'll still call morestack, which will double the stack
   169  	// unnecessarily. See issue #35470.
   170  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
   171  
   172  	q := (*obj.Prog)(nil)
   173  	if framesize <= abi.StackSmall {
   174  		// small stack: SP < stackguard
   175  		//	CMP	stackguard, SP
   176  
   177  		p = obj.Appendp(p, c.newprog)
   178  		p.As = ACMP
   179  		p.From.Type = obj.TYPE_REG
   180  		p.From.Reg = REGRT1
   181  		p.Reg = REGSP
   182  	} else if framesize <= abi.StackBig {
   183  		// large stack: SP-framesize < stackguard-StackSmall
   184  		//	SUB	$(framesize-StackSmall), SP, RT2
   185  		//	CMP	stackguard, RT2
   186  		p = obj.Appendp(p, c.newprog)
   187  
   188  		p.As = ASUB
   189  		p.From.Type = obj.TYPE_CONST
   190  		p.From.Offset = int64(framesize) - abi.StackSmall
   191  		p.Reg = REGSP
   192  		p.To.Type = obj.TYPE_REG
   193  		p.To.Reg = REGRT2
   194  
   195  		p = obj.Appendp(p, c.newprog)
   196  		p.As = ACMP
   197  		p.From.Type = obj.TYPE_REG
   198  		p.From.Reg = REGRT1
   199  		p.Reg = REGRT2
   200  	} else {
   201  		// Such a large stack we need to protect against underflow.
   202  		// The runtime guarantees SP > objabi.StackBig, but
   203  		// framesize is large enough that SP-framesize may
   204  		// underflow, causing a direct comparison with the
   205  		// stack guard to incorrectly succeed. We explicitly
   206  		// guard against underflow.
   207  		//
   208  		//	SUBS	$(framesize-StackSmall), SP, RT2
   209  		//	// On underflow, jump to morestack
   210  		//	BLO	label_of_call_to_morestack
   211  		//	CMP	stackguard, RT2
   212  
   213  		p = obj.Appendp(p, c.newprog)
   214  		p.As = ASUBS
   215  		p.From.Type = obj.TYPE_CONST
   216  		p.From.Offset = int64(framesize) - abi.StackSmall
   217  		p.Reg = REGSP
   218  		p.To.Type = obj.TYPE_REG
   219  		p.To.Reg = REGRT2
   220  
   221  		p = obj.Appendp(p, c.newprog)
   222  		q = p
   223  		p.As = ABLO
   224  		p.To.Type = obj.TYPE_BRANCH
   225  
   226  		p = obj.Appendp(p, c.newprog)
   227  		p.As = ACMP
   228  		p.From.Type = obj.TYPE_REG
   229  		p.From.Reg = REGRT1
   230  		p.Reg = REGRT2
   231  	}
   232  
   233  	// BLS	do-morestack
   234  	bls := obj.Appendp(p, c.newprog)
   235  	bls.As = ABLS
   236  	bls.To.Type = obj.TYPE_BRANCH
   237  
   238  	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
   239  
   240  	var last *obj.Prog
   241  	for last = c.cursym.Func().Text; last.Link != nil; last = last.Link {
   242  	}
   243  
   244  	// Now we are at the end of the function, but logically
   245  	// we are still in function prologue. We need to fix the
   246  	// SP data and PCDATA.
   247  	spfix := obj.Appendp(last, c.newprog)
   248  	spfix.As = obj.ANOP
   249  	spfix.Spadj = -framesize
   250  
   251  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
   252  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
   253  
   254  	if q != nil {
   255  		q.To.SetTarget(pcdata)
   256  	}
   257  	bls.To.SetTarget(pcdata)
   258  
   259  	spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
   260  
   261  	// MOV	LR, R3
   262  	movlr := obj.Appendp(spill, c.newprog)
   263  	movlr.As = AMOVD
   264  	movlr.From.Type = obj.TYPE_REG
   265  	movlr.From.Reg = REGLINK
   266  	movlr.To.Type = obj.TYPE_REG
   267  	movlr.To.Reg = REG_R3
   268  
   269  	debug := movlr
   270  	if false {
   271  		debug = obj.Appendp(debug, c.newprog)
   272  		debug.As = AMOVD
   273  		debug.From.Type = obj.TYPE_CONST
   274  		debug.From.Offset = int64(framesize)
   275  		debug.To.Type = obj.TYPE_REG
   276  		debug.To.Reg = REGTMP
   277  	}
   278  
   279  	// BL	runtime.morestack(SB)
   280  	call := obj.Appendp(debug, c.newprog)
   281  	call.As = ABL
   282  	call.To.Type = obj.TYPE_BRANCH
   283  	morestack := "runtime.morestack"
   284  	switch {
   285  	case c.cursym.CFunc():
   286  		morestack = "runtime.morestackc"
   287  	case !c.cursym.Func().Text.From.Sym.NeedCtxt():
   288  		morestack = "runtime.morestack_noctxt"
   289  	}
   290  	call.To.Sym = c.ctxt.Lookup(morestack)
   291  
   292  	// The instructions which unspill regs should be preemptible.
   293  	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
   294  	unspill := c.cursym.Func().UnspillRegisterArgs(pcdata, c.newprog)
   295  
   296  	// B	start
   297  	jmp := obj.Appendp(unspill, c.newprog)
   298  	jmp.As = AB
   299  	jmp.To.Type = obj.TYPE_BRANCH
   300  	jmp.To.SetTarget(startPred.Link)
   301  	jmp.Spadj = +framesize
   302  
   303  	return end
   304  }
   305  
   306  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
   307  	c := ctxt7{ctxt: ctxt, newprog: newprog}
   308  
   309  	p.From.Class = 0
   310  	p.To.Class = 0
   311  
   312  	// Previously we rewrote $0 to ZR, but we have now removed this change.
   313  	// In order to be compatible with some previous legal instruction formats,
   314  	// reserve the previous conversion for some specific instructions.
   315  	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 && zrReplace[p.As] {
   316  		p.From.Type = obj.TYPE_REG
   317  		p.From.Reg = REGZERO
   318  	}
   319  
   320  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
   321  	switch p.As {
   322  	case AB,
   323  		ABL,
   324  		obj.ARET,
   325  		obj.ADUFFZERO,
   326  		obj.ADUFFCOPY:
   327  		if p.To.Sym != nil {
   328  			p.To.Type = obj.TYPE_BRANCH
   329  		}
   330  		break
   331  	}
   332  
   333  	// Rewrite float and vector constants to values stored in memory.
   334  	switch p.As {
   335  	case AVMOVS:
   336  		if p.From.Type == obj.TYPE_CONST {
   337  			p.From.Type = obj.TYPE_MEM
   338  			p.From.Sym = c.ctxt.Int32Sym(p.From.Offset)
   339  			p.From.Name = obj.NAME_EXTERN
   340  			p.From.Offset = 0
   341  		}
   342  
   343  	case AVMOVD:
   344  		if p.From.Type == obj.TYPE_CONST {
   345  			p.From.Type = obj.TYPE_MEM
   346  			p.From.Sym = c.ctxt.Int64Sym(p.From.Offset)
   347  			p.From.Name = obj.NAME_EXTERN
   348  			p.From.Offset = 0
   349  		}
   350  
   351  	case AVMOVQ:
   352  		if p.From.Type == obj.TYPE_CONST {
   353  			p.From.Type = obj.TYPE_MEM
   354  			p.From.Sym = c.ctxt.Int128Sym(p.GetFrom3().Offset, p.From.Offset)
   355  			p.From.Name = obj.NAME_EXTERN
   356  			p.From.Offset = 0
   357  			p.RestArgs = nil
   358  		}
   359  
   360  	case AFMOVS:
   361  		if p.From.Type == obj.TYPE_FCONST {
   362  			f64 := p.From.Val.(float64)
   363  			f32 := float32(f64)
   364  			if c.chipfloat7(f64) > 0 {
   365  				break
   366  			}
   367  			if math.Float32bits(f32) == 0 {
   368  				p.From.Type = obj.TYPE_REG
   369  				p.From.Reg = REGZERO
   370  				break
   371  			}
   372  			p.From.Type = obj.TYPE_MEM
   373  			p.From.Sym = c.ctxt.Float32Sym(f32)
   374  			p.From.Name = obj.NAME_EXTERN
   375  			p.From.Offset = 0
   376  		}
   377  
   378  	case AFMOVD:
   379  		if p.From.Type == obj.TYPE_FCONST {
   380  			f64 := p.From.Val.(float64)
   381  			if c.chipfloat7(f64) > 0 {
   382  				break
   383  			}
   384  			if math.Float64bits(f64) == 0 {
   385  				p.From.Type = obj.TYPE_REG
   386  				p.From.Reg = REGZERO
   387  				break
   388  			}
   389  			p.From.Type = obj.TYPE_MEM
   390  			p.From.Sym = c.ctxt.Float64Sym(f64)
   391  			p.From.Name = obj.NAME_EXTERN
   392  			p.From.Offset = 0
   393  		}
   394  	}
   395  
   396  	if c.ctxt.Flag_dynlink {
   397  		c.rewriteToUseGot(p)
   398  	}
   399  }
   400  
   401  // Rewrite p, if necessary, to access global data via the global offset table.
   402  func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
   403  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   404  		//     ADUFFxxx $offset
   405  		// becomes
   406  		//     MOVD runtime.duffxxx@GOT, REGTMP
   407  		//     ADD $offset, REGTMP
   408  		//     CALL REGTMP
   409  		var sym *obj.LSym
   410  		if p.As == obj.ADUFFZERO {
   411  			sym = c.ctxt.LookupABI("runtime.duffzero", obj.ABIInternal)
   412  		} else {
   413  			sym = c.ctxt.LookupABI("runtime.duffcopy", obj.ABIInternal)
   414  		}
   415  		offset := p.To.Offset
   416  		p.As = AMOVD
   417  		p.From.Type = obj.TYPE_MEM
   418  		p.From.Name = obj.NAME_GOTREF
   419  		p.From.Sym = sym
   420  		p.To.Type = obj.TYPE_REG
   421  		p.To.Reg = REGTMP
   422  		p.To.Name = obj.NAME_NONE
   423  		p.To.Offset = 0
   424  		p.To.Sym = nil
   425  		p1 := obj.Appendp(p, c.newprog)
   426  		p1.As = AADD
   427  		p1.From.Type = obj.TYPE_CONST
   428  		p1.From.Offset = offset
   429  		p1.To.Type = obj.TYPE_REG
   430  		p1.To.Reg = REGTMP
   431  		p2 := obj.Appendp(p1, c.newprog)
   432  		p2.As = obj.ACALL
   433  		p2.To.Type = obj.TYPE_REG
   434  		p2.To.Reg = REGTMP
   435  	}
   436  
   437  	// We only care about global data: NAME_EXTERN means a global
   438  	// symbol in the Go sense, and p.Sym.Local is true for a few
   439  	// internally defined symbols.
   440  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   441  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   442  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   443  		if p.As != AMOVD {
   444  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   445  		}
   446  		if p.To.Type != obj.TYPE_REG {
   447  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   448  		}
   449  		p.From.Type = obj.TYPE_MEM
   450  		p.From.Name = obj.NAME_GOTREF
   451  		if p.From.Offset != 0 {
   452  			q := obj.Appendp(p, c.newprog)
   453  			q.As = AADD
   454  			q.From.Type = obj.TYPE_CONST
   455  			q.From.Offset = p.From.Offset
   456  			q.To = p.To
   457  			p.From.Offset = 0
   458  		}
   459  	}
   460  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   461  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   462  	}
   463  	var source *obj.Addr
   464  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   465  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   466  	// An addition may be inserted between the two MOVs if there is an offset.
   467  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   468  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   469  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   470  		}
   471  		source = &p.From
   472  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   473  		source = &p.To
   474  	} else {
   475  		return
   476  	}
   477  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   478  		return
   479  	}
   480  	if source.Sym.Type == objabi.STLSBSS {
   481  		return
   482  	}
   483  	if source.Type != obj.TYPE_MEM {
   484  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   485  	}
   486  	p1 := obj.Appendp(p, c.newprog)
   487  	p2 := obj.Appendp(p1, c.newprog)
   488  	p1.As = AMOVD
   489  	p1.From.Type = obj.TYPE_MEM
   490  	p1.From.Sym = source.Sym
   491  	p1.From.Name = obj.NAME_GOTREF
   492  	p1.To.Type = obj.TYPE_REG
   493  	p1.To.Reg = REGTMP
   494  
   495  	p2.As = p.As
   496  	p2.From = p.From
   497  	p2.To = p.To
   498  	if p.From.Name == obj.NAME_EXTERN {
   499  		p2.From.Reg = REGTMP
   500  		p2.From.Name = obj.NAME_NONE
   501  		p2.From.Sym = nil
   502  	} else if p.To.Name == obj.NAME_EXTERN {
   503  		p2.To.Reg = REGTMP
   504  		p2.To.Name = obj.NAME_NONE
   505  		p2.To.Sym = nil
   506  	} else {
   507  		return
   508  	}
   509  	obj.Nopout(p)
   510  }
   511  
   512  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   513  	if cursym.Func().Text == nil || cursym.Func().Text.Link == nil {
   514  		return
   515  	}
   516  
   517  	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
   518  
   519  	p := c.cursym.Func().Text
   520  	textstksiz := p.To.Offset
   521  	if textstksiz == -8 {
   522  		// Historical way to mark NOFRAME.
   523  		p.From.Sym.Set(obj.AttrNoFrame, true)
   524  		textstksiz = 0
   525  	}
   526  	if textstksiz < 0 {
   527  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
   528  	}
   529  	if p.From.Sym.NoFrame() {
   530  		if textstksiz != 0 {
   531  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   532  		}
   533  	}
   534  
   535  	c.cursym.Func().Args = p.To.Val.(int32)
   536  	c.cursym.Func().Locals = int32(textstksiz)
   537  
   538  	/*
   539  	 * find leaf subroutines
   540  	 */
   541  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   542  		switch p.As {
   543  		case obj.ATEXT:
   544  			p.Mark |= LEAF
   545  
   546  		case ABL,
   547  			obj.ADUFFZERO,
   548  			obj.ADUFFCOPY:
   549  			c.cursym.Func().Text.Mark &^= LEAF
   550  		}
   551  	}
   552  
   553  	var q *obj.Prog
   554  	var q1 *obj.Prog
   555  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   556  		o := p.As
   557  		switch o {
   558  		case obj.ATEXT:
   559  			c.cursym.Func().Text = p
   560  			c.autosize = int32(textstksiz)
   561  
   562  			if p.Mark&LEAF != 0 && c.autosize == 0 {
   563  				// A leaf function with no locals has no frame.
   564  				p.From.Sym.Set(obj.AttrNoFrame, true)
   565  			}
   566  
   567  			if !p.From.Sym.NoFrame() {
   568  				// If there is a stack frame at all, it includes
   569  				// space to save the LR.
   570  				c.autosize += 8
   571  			}
   572  
   573  			if c.autosize != 0 {
   574  				extrasize := int32(0)
   575  				if c.autosize%16 == 8 {
   576  					// Allocate extra 8 bytes on the frame top to save FP
   577  					extrasize = 8
   578  				} else if c.autosize&(16-1) == 0 {
   579  					// Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
   580  					extrasize = 16
   581  				} else {
   582  					c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
   583  				}
   584  				c.autosize += extrasize
   585  				c.cursym.Func().Locals += extrasize
   586  
   587  				// low 32 bits for autosize
   588  				// high 32 bits for extrasize
   589  				p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
   590  			} else {
   591  				// NOFRAME
   592  				p.To.Offset = 0
   593  			}
   594  
   595  			if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
   596  				if c.ctxt.Debugvlog {
   597  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
   598  				}
   599  				c.cursym.Func().Text.Mark |= LEAF
   600  			}
   601  
   602  			if cursym.Func().Text.Mark&LEAF != 0 {
   603  				cursym.Set(obj.AttrLeaf, true)
   604  				if p.From.Sym.NoFrame() {
   605  					break
   606  				}
   607  			}
   608  
   609  			if p.Mark&LEAF != 0 && c.autosize < abi.StackSmall {
   610  				// A leaf function with a small stack can be marked
   611  				// NOSPLIT, avoiding a stack check.
   612  				p.From.Sym.Set(obj.AttrNoSplit, true)
   613  			}
   614  
   615  			if !p.From.Sym.NoSplit() {
   616  				p = c.stacksplit(p, c.autosize) // emit split check
   617  			}
   618  
   619  			var prologueEnd *obj.Prog
   620  
   621  			aoffset := c.autosize
   622  			if aoffset > 0xf0 {
   623  				// MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
   624  				// so the maximum aoffset value is 0xf0.
   625  				aoffset = 0xf0
   626  			}
   627  
   628  			// Frame is non-empty. Make sure to save link register, even if
   629  			// it is a leaf function, so that traceback works.
   630  			q = p
   631  			if c.autosize > aoffset {
   632  				// Frame size is too large for a MOVD.W instruction. Store the frame pointer
   633  				// register and link register before decrementing SP, so if a signal comes
   634  				// during the execution of the function prologue, the traceback code will
   635  				// not see a half-updated stack frame.
   636  
   637  				// SUB $autosize, RSP, R20
   638  				q1 = obj.Appendp(q, c.newprog)
   639  				q1.Pos = p.Pos
   640  				q1.As = ASUB
   641  				q1.From.Type = obj.TYPE_CONST
   642  				q1.From.Offset = int64(c.autosize)
   643  				q1.Reg = REGSP
   644  				q1.To.Type = obj.TYPE_REG
   645  				q1.To.Reg = REG_R20
   646  
   647  				prologueEnd = q1
   648  
   649  				// STP (R29, R30), -8(R20)
   650  				q1 = obj.Appendp(q1, c.newprog)
   651  				q1.Pos = p.Pos
   652  				q1.As = ASTP
   653  				q1.From.Type = obj.TYPE_REGREG
   654  				q1.From.Reg = REGFP
   655  				q1.From.Offset = REGLINK
   656  				q1.To.Type = obj.TYPE_MEM
   657  				q1.To.Reg = REG_R20
   658  				q1.To.Offset = -8
   659  
   660  				// This is not async preemptible, as if we open a frame
   661  				// at the current SP, it will clobber the saved LR.
   662  				q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
   663  
   664  				// MOVD R20, RSP
   665  				q1 = obj.Appendp(q1, c.newprog)
   666  				q1.Pos = p.Pos
   667  				q1.As = AMOVD
   668  				q1.From.Type = obj.TYPE_REG
   669  				q1.From.Reg = REG_R20
   670  				q1.To.Type = obj.TYPE_REG
   671  				q1.To.Reg = REGSP
   672  				q1.Spadj = c.autosize
   673  
   674  				q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
   675  
   676  				if buildcfg.GOOS == "ios" {
   677  					// iOS does not support SA_ONSTACK. We will run the signal handler
   678  					// on the G stack. If we write below SP, it may be clobbered by
   679  					// the signal handler. So we save FP and LR after decrementing SP.
   680  					// STP (R29, R30), -8(RSP)
   681  					q1 = obj.Appendp(q1, c.newprog)
   682  					q1.Pos = p.Pos
   683  					q1.As = ASTP
   684  					q1.From.Type = obj.TYPE_REGREG
   685  					q1.From.Reg = REGFP
   686  					q1.From.Offset = REGLINK
   687  					q1.To.Type = obj.TYPE_MEM
   688  					q1.To.Reg = REGSP
   689  					q1.To.Offset = -8
   690  				}
   691  			} else {
   692  				// small frame, update SP and save LR in a single MOVD.W instruction.
   693  				// So if a signal comes during the execution of the function prologue,
   694  				// the traceback code will not see a half-updated stack frame.
   695  				// Also, on Linux, in a cgo binary we may get a SIGSETXID signal
   696  				// early on before the signal stack is set, as glibc doesn't allow
   697  				// us to block SIGSETXID. So it is important that we don't write below
   698  				// the SP until the signal stack is set.
   699  				// Luckily, all the functions from thread entry to setting the signal
   700  				// stack have small frames.
   701  				q1 = obj.Appendp(q, c.newprog)
   702  				q1.As = AMOVD
   703  				q1.Pos = p.Pos
   704  				q1.From.Type = obj.TYPE_REG
   705  				q1.From.Reg = REGLINK
   706  				q1.To.Type = obj.TYPE_MEM
   707  				q1.Scond = C_XPRE
   708  				q1.To.Offset = int64(-aoffset)
   709  				q1.To.Reg = REGSP
   710  				q1.Spadj = aoffset
   711  
   712  				prologueEnd = q1
   713  
   714  				// Frame pointer.
   715  				q1 = obj.Appendp(q1, c.newprog)
   716  				q1.Pos = p.Pos
   717  				q1.As = AMOVD
   718  				q1.From.Type = obj.TYPE_REG
   719  				q1.From.Reg = REGFP
   720  				q1.To.Type = obj.TYPE_MEM
   721  				q1.To.Reg = REGSP
   722  				q1.To.Offset = -8
   723  			}
   724  
   725  			prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
   726  
   727  			q1 = obj.Appendp(q1, c.newprog)
   728  			q1.Pos = p.Pos
   729  			q1.As = ASUB
   730  			q1.From.Type = obj.TYPE_CONST
   731  			q1.From.Offset = 8
   732  			q1.Reg = REGSP
   733  			q1.To.Type = obj.TYPE_REG
   734  			q1.To.Reg = REGFP
   735  
   736  			if c.cursym.Func().Text.From.Sym.Wrapper() {
   737  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   738  				//
   739  				//	MOV  g_panic(g), RT1
   740  				//	CBNZ checkargp
   741  				// end:
   742  				//	NOP
   743  				// ... function body ...
   744  				// checkargp:
   745  				//	MOV  panic_argp(RT1), RT2
   746  				//	ADD  $(autosize+8), RSP, R20
   747  				//	CMP  RT2, R20
   748  				//	BNE  end
   749  				//	ADD  $8, RSP, R20
   750  				//	MOVD R20, panic_argp(RT1)
   751  				//	B    end
   752  				//
   753  				// The NOP is needed to give the jumps somewhere to land.
   754  				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
   755  				q = q1
   756  
   757  				// MOV g_panic(g), RT1
   758  				q = obj.Appendp(q, c.newprog)
   759  				q.As = AMOVD
   760  				q.From.Type = obj.TYPE_MEM
   761  				q.From.Reg = REGG
   762  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   763  				q.To.Type = obj.TYPE_REG
   764  				q.To.Reg = REGRT1
   765  
   766  				// CBNZ RT1, checkargp
   767  				cbnz := obj.Appendp(q, c.newprog)
   768  				cbnz.As = ACBNZ
   769  				cbnz.From.Type = obj.TYPE_REG
   770  				cbnz.From.Reg = REGRT1
   771  				cbnz.To.Type = obj.TYPE_BRANCH
   772  
   773  				// Empty branch target at the top of the function body
   774  				end := obj.Appendp(cbnz, c.newprog)
   775  				end.As = obj.ANOP
   776  
   777  				// find the end of the function
   778  				var last *obj.Prog
   779  				for last = end; last.Link != nil; last = last.Link {
   780  				}
   781  
   782  				// MOV panic_argp(RT1), RT2
   783  				mov := obj.Appendp(last, c.newprog)
   784  				mov.As = AMOVD
   785  				mov.From.Type = obj.TYPE_MEM
   786  				mov.From.Reg = REGRT1
   787  				mov.From.Offset = 0 // Panic.argp
   788  				mov.To.Type = obj.TYPE_REG
   789  				mov.To.Reg = REGRT2
   790  
   791  				// CBNZ branches to the MOV above
   792  				cbnz.To.SetTarget(mov)
   793  
   794  				// ADD $(autosize+8), SP, R20
   795  				q = obj.Appendp(mov, c.newprog)
   796  				q.As = AADD
   797  				q.From.Type = obj.TYPE_CONST
   798  				q.From.Offset = int64(c.autosize) + 8
   799  				q.Reg = REGSP
   800  				q.To.Type = obj.TYPE_REG
   801  				q.To.Reg = REG_R20
   802  
   803  				// CMP RT2, R20
   804  				q = obj.Appendp(q, c.newprog)
   805  				q.As = ACMP
   806  				q.From.Type = obj.TYPE_REG
   807  				q.From.Reg = REGRT2
   808  				q.Reg = REG_R20
   809  
   810  				// BNE end
   811  				q = obj.Appendp(q, c.newprog)
   812  				q.As = ABNE
   813  				q.To.Type = obj.TYPE_BRANCH
   814  				q.To.SetTarget(end)
   815  
   816  				// ADD $8, SP, R20
   817  				q = obj.Appendp(q, c.newprog)
   818  				q.As = AADD
   819  				q.From.Type = obj.TYPE_CONST
   820  				q.From.Offset = 8
   821  				q.Reg = REGSP
   822  				q.To.Type = obj.TYPE_REG
   823  				q.To.Reg = REG_R20
   824  
   825  				// MOV R20, panic_argp(RT1)
   826  				q = obj.Appendp(q, c.newprog)
   827  				q.As = AMOVD
   828  				q.From.Type = obj.TYPE_REG
   829  				q.From.Reg = REG_R20
   830  				q.To.Type = obj.TYPE_MEM
   831  				q.To.Reg = REGRT1
   832  				q.To.Offset = 0 // Panic.argp
   833  
   834  				// B end
   835  				q = obj.Appendp(q, c.newprog)
   836  				q.As = AB
   837  				q.To.Type = obj.TYPE_BRANCH
   838  				q.To.SetTarget(end)
   839  			}
   840  
   841  		case obj.ARET:
   842  			nocache(p)
   843  			if p.From.Type == obj.TYPE_CONST {
   844  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   845  				break
   846  			}
   847  
   848  			retJMP, retReg := p.To.Sym, p.To.Reg
   849  			if retReg == 0 {
   850  				retReg = REGLINK
   851  			}
   852  			p.To = obj.Addr{}
   853  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   854  				if c.autosize != 0 {
   855  					// Restore frame pointer.
   856  					// ADD $framesize-8, RSP, R29
   857  					p.As = AADD
   858  					p.From.Type = obj.TYPE_CONST
   859  					p.From.Offset = int64(c.autosize) - 8
   860  					p.Reg = REGSP
   861  					p.To.Type = obj.TYPE_REG
   862  					p.To.Reg = REGFP
   863  
   864  					// Pop stack frame.
   865  					// ADD $framesize, RSP, RSP
   866  					p = obj.Appendp(p, c.newprog)
   867  					p.As = AADD
   868  					p.From.Type = obj.TYPE_CONST
   869  					p.From.Offset = int64(c.autosize)
   870  					p.To.Type = obj.TYPE_REG
   871  					p.To.Reg = REGSP
   872  					p.Spadj = -c.autosize
   873  				}
   874  			} else {
   875  				aoffset := c.autosize
   876  				// LDP -8(RSP), (R29, R30)
   877  				p.As = ALDP
   878  				p.From.Type = obj.TYPE_MEM
   879  				p.From.Offset = -8
   880  				p.From.Reg = REGSP
   881  				p.To.Type = obj.TYPE_REGREG
   882  				p.To.Reg = REGFP
   883  				p.To.Offset = REGLINK
   884  
   885  				// ADD $aoffset, RSP, RSP
   886  				q = newprog()
   887  				q.As = AADD
   888  				q.From.Type = obj.TYPE_CONST
   889  				q.From.Offset = int64(aoffset)
   890  				q.To.Type = obj.TYPE_REG
   891  				q.To.Reg = REGSP
   892  				q.Spadj = -aoffset
   893  				q.Pos = p.Pos
   894  				q.Link = p.Link
   895  				p.Link = q
   896  				p = q
   897  			}
   898  
   899  			// If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
   900  			// so that if you are debugging a low-level crash where PC and LR are zero,
   901  			// you can look at R27 to see what jumped to the zero.
   902  			// This is useful when bringing up Go on a new system.
   903  			// (There is similar code in ../ppc64/obj9.go:/if.false.)
   904  			const debugRETZERO = false
   905  			if debugRETZERO {
   906  				if p.As != obj.ARET {
   907  					q = newprog()
   908  					q.Pos = p.Pos
   909  					q.Link = p.Link
   910  					p.Link = q
   911  					p = q
   912  				}
   913  				p.As = AADR
   914  				p.From.Type = obj.TYPE_BRANCH
   915  				p.From.Offset = 0
   916  				p.To.Type = obj.TYPE_REG
   917  				p.To.Reg = REGTMP
   918  
   919  			}
   920  
   921  			if p.As != obj.ARET {
   922  				q = newprog()
   923  				q.Pos = p.Pos
   924  				q.Link = p.Link
   925  				p.Link = q
   926  				p = q
   927  			}
   928  
   929  			if retJMP != nil {
   930  				p.As = AB
   931  				p.To.Type = obj.TYPE_BRANCH
   932  				p.To.Sym = retJMP
   933  				p.Spadj = +c.autosize
   934  				break
   935  			}
   936  
   937  			p.As = obj.ARET
   938  			p.To.Type = obj.TYPE_MEM
   939  			p.To.Offset = 0
   940  			p.To.Reg = retReg
   941  			p.Spadj = +c.autosize
   942  
   943  		case AADD, ASUB:
   944  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   945  				if p.As == AADD {
   946  					p.Spadj = int32(-p.From.Offset)
   947  				} else {
   948  					p.Spadj = int32(+p.From.Offset)
   949  				}
   950  			}
   951  
   952  		case obj.AGETCALLERPC:
   953  			if cursym.Leaf() {
   954  				/* MOVD LR, Rd */
   955  				p.As = AMOVD
   956  				p.From.Type = obj.TYPE_REG
   957  				p.From.Reg = REGLINK
   958  			} else {
   959  				/* MOVD (RSP), Rd */
   960  				p.As = AMOVD
   961  				p.From.Type = obj.TYPE_MEM
   962  				p.From.Reg = REGSP
   963  			}
   964  
   965  		case obj.ADUFFCOPY:
   966  			//  ADR	ret_addr, R27
   967  			//  STP	(FP, R27), -24(SP)
   968  			//  SUB	24, SP, FP
   969  			//  DUFFCOPY
   970  			// ret_addr:
   971  			//  SUB	8, SP, FP
   972  
   973  			q1 := p
   974  			// copy DUFFCOPY from q1 to q4
   975  			q4 := obj.Appendp(p, c.newprog)
   976  			q4.Pos = p.Pos
   977  			q4.As = obj.ADUFFCOPY
   978  			q4.To = p.To
   979  
   980  			q1.As = AADR
   981  			q1.From.Type = obj.TYPE_BRANCH
   982  			q1.To.Type = obj.TYPE_REG
   983  			q1.To.Reg = REG_R27
   984  
   985  			q2 := obj.Appendp(q1, c.newprog)
   986  			q2.Pos = p.Pos
   987  			q2.As = ASTP
   988  			q2.From.Type = obj.TYPE_REGREG
   989  			q2.From.Reg = REGFP
   990  			q2.From.Offset = int64(REG_R27)
   991  			q2.To.Type = obj.TYPE_MEM
   992  			q2.To.Reg = REGSP
   993  			q2.To.Offset = -24
   994  
   995  			// maintain FP for DUFFCOPY
   996  			q3 := obj.Appendp(q2, c.newprog)
   997  			q3.Pos = p.Pos
   998  			q3.As = ASUB
   999  			q3.From.Type = obj.TYPE_CONST
  1000  			q3.From.Offset = 24
  1001  			q3.Reg = REGSP
  1002  			q3.To.Type = obj.TYPE_REG
  1003  			q3.To.Reg = REGFP
  1004  
  1005  			q5 := obj.Appendp(q4, c.newprog)
  1006  			q5.Pos = p.Pos
  1007  			q5.As = ASUB
  1008  			q5.From.Type = obj.TYPE_CONST
  1009  			q5.From.Offset = 8
  1010  			q5.Reg = REGSP
  1011  			q5.To.Type = obj.TYPE_REG
  1012  			q5.To.Reg = REGFP
  1013  			q1.From.SetTarget(q5)
  1014  			p = q5
  1015  
  1016  		case obj.ADUFFZERO:
  1017  			//  ADR	ret_addr, R27
  1018  			//  STP	(FP, R27), -24(SP)
  1019  			//  SUB	24, SP, FP
  1020  			//  DUFFZERO
  1021  			// ret_addr:
  1022  			//  SUB	8, SP, FP
  1023  
  1024  			q1 := p
  1025  			// copy DUFFZERO from q1 to q4
  1026  			q4 := obj.Appendp(p, c.newprog)
  1027  			q4.Pos = p.Pos
  1028  			q4.As = obj.ADUFFZERO
  1029  			q4.To = p.To
  1030  
  1031  			q1.As = AADR
  1032  			q1.From.Type = obj.TYPE_BRANCH
  1033  			q1.To.Type = obj.TYPE_REG
  1034  			q1.To.Reg = REG_R27
  1035  
  1036  			q2 := obj.Appendp(q1, c.newprog)
  1037  			q2.Pos = p.Pos
  1038  			q2.As = ASTP
  1039  			q2.From.Type = obj.TYPE_REGREG
  1040  			q2.From.Reg = REGFP
  1041  			q2.From.Offset = int64(REG_R27)
  1042  			q2.To.Type = obj.TYPE_MEM
  1043  			q2.To.Reg = REGSP
  1044  			q2.To.Offset = -24
  1045  
  1046  			// maintain FP for DUFFZERO
  1047  			q3 := obj.Appendp(q2, c.newprog)
  1048  			q3.Pos = p.Pos
  1049  			q3.As = ASUB
  1050  			q3.From.Type = obj.TYPE_CONST
  1051  			q3.From.Offset = 24
  1052  			q3.Reg = REGSP
  1053  			q3.To.Type = obj.TYPE_REG
  1054  			q3.To.Reg = REGFP
  1055  
  1056  			q5 := obj.Appendp(q4, c.newprog)
  1057  			q5.Pos = p.Pos
  1058  			q5.As = ASUB
  1059  			q5.From.Type = obj.TYPE_CONST
  1060  			q5.From.Offset = 8
  1061  			q5.Reg = REGSP
  1062  			q5.To.Type = obj.TYPE_REG
  1063  			q5.To.Reg = REGFP
  1064  			q1.From.SetTarget(q5)
  1065  			p = q5
  1066  		}
  1067  
  1068  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
  1069  			f := c.cursym.Func()
  1070  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
  1071  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
  1072  				if ctxt.Debugvlog || !ctxt.IsAsm {
  1073  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
  1074  					if !ctxt.IsAsm {
  1075  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
  1076  						ctxt.DiagFlush()
  1077  						log.Fatalf("bad SPWRITE")
  1078  					}
  1079  				}
  1080  			}
  1081  		}
  1082  		if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
  1083  			offset := p.From.Offset
  1084  			op := offset & (3 << 22)
  1085  			if op != SHIFT_LL {
  1086  				ctxt.Diag("illegal combination: %v", p)
  1087  			}
  1088  			r := (offset >> 16) & 31
  1089  			shift := (offset >> 10) & 63
  1090  			if shift > 4 {
  1091  				// the shift amount is out of range, in order to avoid repeated error
  1092  				// reportings, don't call ctxt.Diag, because asmout case 27 has the
  1093  				// same check.
  1094  				shift = 7
  1095  			}
  1096  			p.From.Type = obj.TYPE_REG
  1097  			p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
  1098  			p.From.Offset = 0
  1099  		}
  1100  	}
  1101  }
  1102  
  1103  func nocache(p *obj.Prog) {
  1104  	p.Optab = 0
  1105  	p.From.Class = 0
  1106  	p.To.Class = 0
  1107  }
  1108  
  1109  var unaryDst = map[obj.As]bool{
  1110  	AWORD:  true,
  1111  	ADWORD: true,
  1112  	ABL:    true,
  1113  	AB:     true,
  1114  	ACLREX: true,
  1115  }
  1116  
  1117  var Linkarm64 = obj.LinkArch{
  1118  	Arch:           sys.ArchARM64,
  1119  	Init:           buildop,
  1120  	Preprocess:     preprocess,
  1121  	Assemble:       span7,
  1122  	Progedit:       progedit,
  1123  	UnaryDst:       unaryDst,
  1124  	DWARFRegisters: ARM64DWARFRegisters,
  1125  }
  1126  

View as plain text