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  	var retjmp *obj.LSym
   556  	for p := c.cursym.Func().Text; p != nil; p = p.Link {
   557  		o := p.As
   558  		switch o {
   559  		case obj.ATEXT:
   560  			c.cursym.Func().Text = p
   561  			c.autosize = int32(textstksiz)
   562  
   563  			if p.Mark&LEAF != 0 && c.autosize == 0 {
   564  				// A leaf function with no locals has no frame.
   565  				p.From.Sym.Set(obj.AttrNoFrame, true)
   566  			}
   567  
   568  			if !p.From.Sym.NoFrame() {
   569  				// If there is a stack frame at all, it includes
   570  				// space to save the LR.
   571  				c.autosize += 8
   572  			}
   573  
   574  			if c.autosize != 0 {
   575  				extrasize := int32(0)
   576  				if c.autosize%16 == 8 {
   577  					// Allocate extra 8 bytes on the frame top to save FP
   578  					extrasize = 8
   579  				} else if c.autosize&(16-1) == 0 {
   580  					// Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
   581  					extrasize = 16
   582  				} else {
   583  					c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
   584  				}
   585  				c.autosize += extrasize
   586  				c.cursym.Func().Locals += extrasize
   587  
   588  				// low 32 bits for autosize
   589  				// high 32 bits for extrasize
   590  				p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
   591  			} else {
   592  				// NOFRAME
   593  				p.To.Offset = 0
   594  			}
   595  
   596  			if c.autosize == 0 && c.cursym.Func().Text.Mark&LEAF == 0 {
   597  				if c.ctxt.Debugvlog {
   598  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func().Text.From.Sym.Name)
   599  				}
   600  				c.cursym.Func().Text.Mark |= LEAF
   601  			}
   602  
   603  			if cursym.Func().Text.Mark&LEAF != 0 {
   604  				cursym.Set(obj.AttrLeaf, true)
   605  				if p.From.Sym.NoFrame() {
   606  					break
   607  				}
   608  			}
   609  
   610  			if p.Mark&LEAF != 0 && c.autosize < abi.StackSmall {
   611  				// A leaf function with a small stack can be marked
   612  				// NOSPLIT, avoiding a stack check.
   613  				p.From.Sym.Set(obj.AttrNoSplit, true)
   614  			}
   615  
   616  			if !p.From.Sym.NoSplit() {
   617  				p = c.stacksplit(p, c.autosize) // emit split check
   618  			}
   619  
   620  			var prologueEnd *obj.Prog
   621  
   622  			aoffset := c.autosize
   623  			if aoffset > 0xf0 {
   624  				// MOVD.W offset variant range is -0x100 to 0xf8, SP should be 16-byte aligned.
   625  				// so the maximum aoffset value is 0xf0.
   626  				aoffset = 0xf0
   627  			}
   628  
   629  			// Frame is non-empty. Make sure to save link register, even if
   630  			// it is a leaf function, so that traceback works.
   631  			q = p
   632  			if c.autosize > aoffset {
   633  				// Frame size is too large for a MOVD.W instruction. Store the frame pointer
   634  				// register and link register before decrementing SP, so if a signal comes
   635  				// during the execution of the function prologue, the traceback code will
   636  				// not see a half-updated stack frame.
   637  
   638  				// SUB $autosize, RSP, R20
   639  				q1 = obj.Appendp(q, c.newprog)
   640  				q1.Pos = p.Pos
   641  				q1.As = ASUB
   642  				q1.From.Type = obj.TYPE_CONST
   643  				q1.From.Offset = int64(c.autosize)
   644  				q1.Reg = REGSP
   645  				q1.To.Type = obj.TYPE_REG
   646  				q1.To.Reg = REG_R20
   647  
   648  				prologueEnd = q1
   649  
   650  				// STP (R29, R30), -8(R20)
   651  				q1 = obj.Appendp(q1, c.newprog)
   652  				q1.Pos = p.Pos
   653  				q1.As = ASTP
   654  				q1.From.Type = obj.TYPE_REGREG
   655  				q1.From.Reg = REGFP
   656  				q1.From.Offset = REGLINK
   657  				q1.To.Type = obj.TYPE_MEM
   658  				q1.To.Reg = REG_R20
   659  				q1.To.Offset = -8
   660  
   661  				// This is not async preemptible, as if we open a frame
   662  				// at the current SP, it will clobber the saved LR.
   663  				q1 = c.ctxt.StartUnsafePoint(q1, c.newprog)
   664  
   665  				// MOVD R20, RSP
   666  				q1 = obj.Appendp(q1, c.newprog)
   667  				q1.Pos = p.Pos
   668  				q1.As = AMOVD
   669  				q1.From.Type = obj.TYPE_REG
   670  				q1.From.Reg = REG_R20
   671  				q1.To.Type = obj.TYPE_REG
   672  				q1.To.Reg = REGSP
   673  				q1.Spadj = c.autosize
   674  
   675  				q1 = c.ctxt.EndUnsafePoint(q1, c.newprog, -1)
   676  
   677  				if buildcfg.GOOS == "ios" {
   678  					// iOS does not support SA_ONSTACK. We will run the signal handler
   679  					// on the G stack. If we write below SP, it may be clobbered by
   680  					// the signal handler. So we save FP and LR after decrementing SP.
   681  					// STP (R29, R30), -8(RSP)
   682  					q1 = obj.Appendp(q1, c.newprog)
   683  					q1.Pos = p.Pos
   684  					q1.As = ASTP
   685  					q1.From.Type = obj.TYPE_REGREG
   686  					q1.From.Reg = REGFP
   687  					q1.From.Offset = REGLINK
   688  					q1.To.Type = obj.TYPE_MEM
   689  					q1.To.Reg = REGSP
   690  					q1.To.Offset = -8
   691  				}
   692  			} else {
   693  				// small frame, update SP and save LR in a single MOVD.W instruction.
   694  				// So if a signal comes during the execution of the function prologue,
   695  				// the traceback code will not see a half-updated stack frame.
   696  				// Also, on Linux, in a cgo binary we may get a SIGSETXID signal
   697  				// early on before the signal stack is set, as glibc doesn't allow
   698  				// us to block SIGSETXID. So it is important that we don't write below
   699  				// the SP until the signal stack is set.
   700  				// Luckily, all the functions from thread entry to setting the signal
   701  				// stack have small frames.
   702  				q1 = obj.Appendp(q, c.newprog)
   703  				q1.As = AMOVD
   704  				q1.Pos = p.Pos
   705  				q1.From.Type = obj.TYPE_REG
   706  				q1.From.Reg = REGLINK
   707  				q1.To.Type = obj.TYPE_MEM
   708  				q1.Scond = C_XPRE
   709  				q1.To.Offset = int64(-aoffset)
   710  				q1.To.Reg = REGSP
   711  				q1.Spadj = aoffset
   712  
   713  				prologueEnd = q1
   714  
   715  				// Frame pointer.
   716  				q1 = obj.Appendp(q1, c.newprog)
   717  				q1.Pos = p.Pos
   718  				q1.As = AMOVD
   719  				q1.From.Type = obj.TYPE_REG
   720  				q1.From.Reg = REGFP
   721  				q1.To.Type = obj.TYPE_MEM
   722  				q1.To.Reg = REGSP
   723  				q1.To.Offset = -8
   724  			}
   725  
   726  			prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
   727  
   728  			q1 = obj.Appendp(q1, c.newprog)
   729  			q1.Pos = p.Pos
   730  			q1.As = ASUB
   731  			q1.From.Type = obj.TYPE_CONST
   732  			q1.From.Offset = 8
   733  			q1.Reg = REGSP
   734  			q1.To.Type = obj.TYPE_REG
   735  			q1.To.Reg = REGFP
   736  
   737  			if c.cursym.Func().Text.From.Sym.Wrapper() {
   738  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   739  				//
   740  				//	MOV  g_panic(g), RT1
   741  				//	CBNZ checkargp
   742  				// end:
   743  				//	NOP
   744  				// ... function body ...
   745  				// checkargp:
   746  				//	MOV  panic_argp(RT1), RT2
   747  				//	ADD  $(autosize+8), RSP, R20
   748  				//	CMP  RT2, R20
   749  				//	BNE  end
   750  				//	ADD  $8, RSP, R20
   751  				//	MOVD R20, panic_argp(RT1)
   752  				//	B    end
   753  				//
   754  				// The NOP is needed to give the jumps somewhere to land.
   755  				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
   756  				q = q1
   757  
   758  				// MOV g_panic(g), RT1
   759  				q = obj.Appendp(q, c.newprog)
   760  				q.As = AMOVD
   761  				q.From.Type = obj.TYPE_MEM
   762  				q.From.Reg = REGG
   763  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   764  				q.To.Type = obj.TYPE_REG
   765  				q.To.Reg = REGRT1
   766  
   767  				// CBNZ RT1, checkargp
   768  				cbnz := obj.Appendp(q, c.newprog)
   769  				cbnz.As = ACBNZ
   770  				cbnz.From.Type = obj.TYPE_REG
   771  				cbnz.From.Reg = REGRT1
   772  				cbnz.To.Type = obj.TYPE_BRANCH
   773  
   774  				// Empty branch target at the top of the function body
   775  				end := obj.Appendp(cbnz, c.newprog)
   776  				end.As = obj.ANOP
   777  
   778  				// find the end of the function
   779  				var last *obj.Prog
   780  				for last = end; last.Link != nil; last = last.Link {
   781  				}
   782  
   783  				// MOV panic_argp(RT1), RT2
   784  				mov := obj.Appendp(last, c.newprog)
   785  				mov.As = AMOVD
   786  				mov.From.Type = obj.TYPE_MEM
   787  				mov.From.Reg = REGRT1
   788  				mov.From.Offset = 0 // Panic.argp
   789  				mov.To.Type = obj.TYPE_REG
   790  				mov.To.Reg = REGRT2
   791  
   792  				// CBNZ branches to the MOV above
   793  				cbnz.To.SetTarget(mov)
   794  
   795  				// ADD $(autosize+8), SP, R20
   796  				q = obj.Appendp(mov, c.newprog)
   797  				q.As = AADD
   798  				q.From.Type = obj.TYPE_CONST
   799  				q.From.Offset = int64(c.autosize) + 8
   800  				q.Reg = REGSP
   801  				q.To.Type = obj.TYPE_REG
   802  				q.To.Reg = REG_R20
   803  
   804  				// CMP RT2, R20
   805  				q = obj.Appendp(q, c.newprog)
   806  				q.As = ACMP
   807  				q.From.Type = obj.TYPE_REG
   808  				q.From.Reg = REGRT2
   809  				q.Reg = REG_R20
   810  
   811  				// BNE end
   812  				q = obj.Appendp(q, c.newprog)
   813  				q.As = ABNE
   814  				q.To.Type = obj.TYPE_BRANCH
   815  				q.To.SetTarget(end)
   816  
   817  				// ADD $8, SP, R20
   818  				q = obj.Appendp(q, c.newprog)
   819  				q.As = AADD
   820  				q.From.Type = obj.TYPE_CONST
   821  				q.From.Offset = 8
   822  				q.Reg = REGSP
   823  				q.To.Type = obj.TYPE_REG
   824  				q.To.Reg = REG_R20
   825  
   826  				// MOV R20, panic_argp(RT1)
   827  				q = obj.Appendp(q, c.newprog)
   828  				q.As = AMOVD
   829  				q.From.Type = obj.TYPE_REG
   830  				q.From.Reg = REG_R20
   831  				q.To.Type = obj.TYPE_MEM
   832  				q.To.Reg = REGRT1
   833  				q.To.Offset = 0 // Panic.argp
   834  
   835  				// B end
   836  				q = obj.Appendp(q, c.newprog)
   837  				q.As = AB
   838  				q.To.Type = obj.TYPE_BRANCH
   839  				q.To.SetTarget(end)
   840  			}
   841  
   842  		case obj.ARET:
   843  			nocache(p)
   844  			if p.From.Type == obj.TYPE_CONST {
   845  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   846  				break
   847  			}
   848  
   849  			retjmp = p.To.Sym
   850  			p.To = obj.Addr{}
   851  			if c.cursym.Func().Text.Mark&LEAF != 0 {
   852  				if c.autosize != 0 {
   853  					// Restore frame pointer.
   854  					// ADD $framesize-8, RSP, R29
   855  					p.As = AADD
   856  					p.From.Type = obj.TYPE_CONST
   857  					p.From.Offset = int64(c.autosize) - 8
   858  					p.Reg = REGSP
   859  					p.To.Type = obj.TYPE_REG
   860  					p.To.Reg = REGFP
   861  
   862  					// Pop stack frame.
   863  					// ADD $framesize, RSP, RSP
   864  					p = obj.Appendp(p, c.newprog)
   865  					p.As = AADD
   866  					p.From.Type = obj.TYPE_CONST
   867  					p.From.Offset = int64(c.autosize)
   868  					p.To.Type = obj.TYPE_REG
   869  					p.To.Reg = REGSP
   870  					p.Spadj = -c.autosize
   871  				}
   872  			} else {
   873  				aoffset := c.autosize
   874  				// LDP -8(RSP), (R29, R30)
   875  				p.As = ALDP
   876  				p.From.Type = obj.TYPE_MEM
   877  				p.From.Offset = -8
   878  				p.From.Reg = REGSP
   879  				p.To.Type = obj.TYPE_REGREG
   880  				p.To.Reg = REGFP
   881  				p.To.Offset = REGLINK
   882  
   883  				// ADD $aoffset, RSP, RSP
   884  				q = newprog()
   885  				q.As = AADD
   886  				q.From.Type = obj.TYPE_CONST
   887  				q.From.Offset = int64(aoffset)
   888  				q.To.Type = obj.TYPE_REG
   889  				q.To.Reg = REGSP
   890  				q.Spadj = -aoffset
   891  				q.Pos = p.Pos
   892  				q.Link = p.Link
   893  				p.Link = q
   894  				p = q
   895  			}
   896  
   897  			// If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
   898  			// so that if you are debugging a low-level crash where PC and LR are zero,
   899  			// you can look at R27 to see what jumped to the zero.
   900  			// This is useful when bringing up Go on a new system.
   901  			// (There is similar code in ../ppc64/obj9.go:/if.false.)
   902  			const debugRETZERO = false
   903  			if debugRETZERO {
   904  				if p.As != obj.ARET {
   905  					q = newprog()
   906  					q.Pos = p.Pos
   907  					q.Link = p.Link
   908  					p.Link = q
   909  					p = q
   910  				}
   911  				p.As = AADR
   912  				p.From.Type = obj.TYPE_BRANCH
   913  				p.From.Offset = 0
   914  				p.To.Type = obj.TYPE_REG
   915  				p.To.Reg = REGTMP
   916  
   917  			}
   918  
   919  			if p.As != obj.ARET {
   920  				q = newprog()
   921  				q.Pos = p.Pos
   922  				q.Link = p.Link
   923  				p.Link = q
   924  				p = q
   925  			}
   926  
   927  			if retjmp != nil { // retjmp
   928  				p.As = AB
   929  				p.To.Type = obj.TYPE_BRANCH
   930  				p.To.Sym = retjmp
   931  				p.Spadj = +c.autosize
   932  				break
   933  			}
   934  
   935  			p.As = obj.ARET
   936  			p.To.Type = obj.TYPE_MEM
   937  			p.To.Offset = 0
   938  			p.To.Reg = REGLINK
   939  			p.Spadj = +c.autosize
   940  
   941  		case AADD, ASUB:
   942  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   943  				if p.As == AADD {
   944  					p.Spadj = int32(-p.From.Offset)
   945  				} else {
   946  					p.Spadj = int32(+p.From.Offset)
   947  				}
   948  			}
   949  
   950  		case obj.AGETCALLERPC:
   951  			if cursym.Leaf() {
   952  				/* MOVD LR, Rd */
   953  				p.As = AMOVD
   954  				p.From.Type = obj.TYPE_REG
   955  				p.From.Reg = REGLINK
   956  			} else {
   957  				/* MOVD (RSP), Rd */
   958  				p.As = AMOVD
   959  				p.From.Type = obj.TYPE_MEM
   960  				p.From.Reg = REGSP
   961  			}
   962  
   963  		case obj.ADUFFCOPY:
   964  			//  ADR	ret_addr, R27
   965  			//  STP	(FP, R27), -24(SP)
   966  			//  SUB	24, SP, FP
   967  			//  DUFFCOPY
   968  			// ret_addr:
   969  			//  SUB	8, SP, FP
   970  
   971  			q1 := p
   972  			// copy DUFFCOPY from q1 to q4
   973  			q4 := obj.Appendp(p, c.newprog)
   974  			q4.Pos = p.Pos
   975  			q4.As = obj.ADUFFCOPY
   976  			q4.To = p.To
   977  
   978  			q1.As = AADR
   979  			q1.From.Type = obj.TYPE_BRANCH
   980  			q1.To.Type = obj.TYPE_REG
   981  			q1.To.Reg = REG_R27
   982  
   983  			q2 := obj.Appendp(q1, c.newprog)
   984  			q2.Pos = p.Pos
   985  			q2.As = ASTP
   986  			q2.From.Type = obj.TYPE_REGREG
   987  			q2.From.Reg = REGFP
   988  			q2.From.Offset = int64(REG_R27)
   989  			q2.To.Type = obj.TYPE_MEM
   990  			q2.To.Reg = REGSP
   991  			q2.To.Offset = -24
   992  
   993  			// maintain FP for DUFFCOPY
   994  			q3 := obj.Appendp(q2, c.newprog)
   995  			q3.Pos = p.Pos
   996  			q3.As = ASUB
   997  			q3.From.Type = obj.TYPE_CONST
   998  			q3.From.Offset = 24
   999  			q3.Reg = REGSP
  1000  			q3.To.Type = obj.TYPE_REG
  1001  			q3.To.Reg = REGFP
  1002  
  1003  			q5 := obj.Appendp(q4, c.newprog)
  1004  			q5.Pos = p.Pos
  1005  			q5.As = ASUB
  1006  			q5.From.Type = obj.TYPE_CONST
  1007  			q5.From.Offset = 8
  1008  			q5.Reg = REGSP
  1009  			q5.To.Type = obj.TYPE_REG
  1010  			q5.To.Reg = REGFP
  1011  			q1.From.SetTarget(q5)
  1012  			p = q5
  1013  
  1014  		case obj.ADUFFZERO:
  1015  			//  ADR	ret_addr, R27
  1016  			//  STP	(FP, R27), -24(SP)
  1017  			//  SUB	24, SP, FP
  1018  			//  DUFFZERO
  1019  			// ret_addr:
  1020  			//  SUB	8, SP, FP
  1021  
  1022  			q1 := p
  1023  			// copy DUFFZERO from q1 to q4
  1024  			q4 := obj.Appendp(p, c.newprog)
  1025  			q4.Pos = p.Pos
  1026  			q4.As = obj.ADUFFZERO
  1027  			q4.To = p.To
  1028  
  1029  			q1.As = AADR
  1030  			q1.From.Type = obj.TYPE_BRANCH
  1031  			q1.To.Type = obj.TYPE_REG
  1032  			q1.To.Reg = REG_R27
  1033  
  1034  			q2 := obj.Appendp(q1, c.newprog)
  1035  			q2.Pos = p.Pos
  1036  			q2.As = ASTP
  1037  			q2.From.Type = obj.TYPE_REGREG
  1038  			q2.From.Reg = REGFP
  1039  			q2.From.Offset = int64(REG_R27)
  1040  			q2.To.Type = obj.TYPE_MEM
  1041  			q2.To.Reg = REGSP
  1042  			q2.To.Offset = -24
  1043  
  1044  			// maintain FP for DUFFZERO
  1045  			q3 := obj.Appendp(q2, c.newprog)
  1046  			q3.Pos = p.Pos
  1047  			q3.As = ASUB
  1048  			q3.From.Type = obj.TYPE_CONST
  1049  			q3.From.Offset = 24
  1050  			q3.Reg = REGSP
  1051  			q3.To.Type = obj.TYPE_REG
  1052  			q3.To.Reg = REGFP
  1053  
  1054  			q5 := obj.Appendp(q4, c.newprog)
  1055  			q5.Pos = p.Pos
  1056  			q5.As = ASUB
  1057  			q5.From.Type = obj.TYPE_CONST
  1058  			q5.From.Offset = 8
  1059  			q5.Reg = REGSP
  1060  			q5.To.Type = obj.TYPE_REG
  1061  			q5.To.Reg = REGFP
  1062  			q1.From.SetTarget(q5)
  1063  			p = q5
  1064  		}
  1065  
  1066  		if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {
  1067  			f := c.cursym.Func()
  1068  			if f.FuncFlag&abi.FuncFlagSPWrite == 0 {
  1069  				c.cursym.Func().FuncFlag |= abi.FuncFlagSPWrite
  1070  				if ctxt.Debugvlog || !ctxt.IsAsm {
  1071  					ctxt.Logf("auto-SPWRITE: %s %v\n", c.cursym.Name, p)
  1072  					if !ctxt.IsAsm {
  1073  						ctxt.Diag("invalid auto-SPWRITE in non-assembly")
  1074  						ctxt.DiagFlush()
  1075  						log.Fatalf("bad SPWRITE")
  1076  					}
  1077  				}
  1078  			}
  1079  		}
  1080  		if p.From.Type == obj.TYPE_SHIFT && (p.To.Reg == REG_RSP || p.Reg == REG_RSP) {
  1081  			offset := p.From.Offset
  1082  			op := offset & (3 << 22)
  1083  			if op != SHIFT_LL {
  1084  				ctxt.Diag("illegal combination: %v", p)
  1085  			}
  1086  			r := (offset >> 16) & 31
  1087  			shift := (offset >> 10) & 63
  1088  			if shift > 4 {
  1089  				// the shift amount is out of range, in order to avoid repeated error
  1090  				// reportings, don't call ctxt.Diag, because asmout case 27 has the
  1091  				// same check.
  1092  				shift = 7
  1093  			}
  1094  			p.From.Type = obj.TYPE_REG
  1095  			p.From.Reg = int16(REG_LSL + r + (shift&7)<<5)
  1096  			p.From.Offset = 0
  1097  		}
  1098  	}
  1099  }
  1100  
  1101  func nocache(p *obj.Prog) {
  1102  	p.Optab = 0
  1103  	p.From.Class = 0
  1104  	p.To.Class = 0
  1105  }
  1106  
  1107  var unaryDst = map[obj.As]bool{
  1108  	AWORD:  true,
  1109  	ADWORD: true,
  1110  	ABL:    true,
  1111  	AB:     true,
  1112  	ACLREX: true,
  1113  }
  1114  
  1115  var Linkarm64 = obj.LinkArch{
  1116  	Arch:           sys.ArchARM64,
  1117  	Init:           buildop,
  1118  	Preprocess:     preprocess,
  1119  	Assemble:       span7,
  1120  	Progedit:       progedit,
  1121  	UnaryDst:       unaryDst,
  1122  	DWARFRegisters: ARM64DWARFRegisters,
  1123  }
  1124  

View as plain text