github.com/bir3/gocompiler@v0.3.205/src/cmd/internal/obj/arm64/obj7.go (about)

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