github.com/slayercat/go@v0.0.0-20170428012452-c51559813f61/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  	"cmd/internal/obj"
    35  	"cmd/internal/objabi"
    36  	"cmd/internal/sys"
    37  	"math"
    38  )
    39  
    40  var complements = []obj.As{
    41  	AADD:  ASUB,
    42  	AADDW: ASUBW,
    43  	ASUB:  AADD,
    44  	ASUBW: AADDW,
    45  	ACMP:  ACMN,
    46  	ACMPW: ACMNW,
    47  	ACMN:  ACMP,
    48  	ACMNW: ACMPW,
    49  }
    50  
    51  func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
    52  	// MOV	g_stackguard(g), R1
    53  	p = obj.Appendp(p, c.newprog)
    54  
    55  	p.As = AMOVD
    56  	p.From.Type = obj.TYPE_MEM
    57  	p.From.Reg = REGG
    58  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
    59  	if c.cursym.CFunc() {
    60  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
    61  	}
    62  	p.To.Type = obj.TYPE_REG
    63  	p.To.Reg = REG_R1
    64  
    65  	q := (*obj.Prog)(nil)
    66  	if framesize <= objabi.StackSmall {
    67  		// small stack: SP < stackguard
    68  		//	MOV	SP, R2
    69  		//	CMP	stackguard, R2
    70  		p = obj.Appendp(p, c.newprog)
    71  
    72  		p.As = AMOVD
    73  		p.From.Type = obj.TYPE_REG
    74  		p.From.Reg = REGSP
    75  		p.To.Type = obj.TYPE_REG
    76  		p.To.Reg = REG_R2
    77  
    78  		p = obj.Appendp(p, c.newprog)
    79  		p.As = ACMP
    80  		p.From.Type = obj.TYPE_REG
    81  		p.From.Reg = REG_R1
    82  		p.Reg = REG_R2
    83  	} else if framesize <= objabi.StackBig {
    84  		// large stack: SP-framesize < stackguard-StackSmall
    85  		//	SUB	$(framesize-StackSmall), SP, R2
    86  		//	CMP	stackguard, R2
    87  		p = obj.Appendp(p, c.newprog)
    88  
    89  		p.As = ASUB
    90  		p.From.Type = obj.TYPE_CONST
    91  		p.From.Offset = int64(framesize) - objabi.StackSmall
    92  		p.Reg = REGSP
    93  		p.To.Type = obj.TYPE_REG
    94  		p.To.Reg = REG_R2
    95  
    96  		p = obj.Appendp(p, c.newprog)
    97  		p.As = ACMP
    98  		p.From.Type = obj.TYPE_REG
    99  		p.From.Reg = REG_R1
   100  		p.Reg = REG_R2
   101  	} else {
   102  		// Such a large stack we need to protect against wraparound
   103  		// if SP is close to zero.
   104  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   105  		// The +StackGuard on both sides is required to keep the left side positive:
   106  		// SP is allowed to be slightly below stackguard. See stack.h.
   107  		//	CMP	$StackPreempt, R1
   108  		//	BEQ	label_of_call_to_morestack
   109  		//	ADD	$StackGuard, SP, R2
   110  		//	SUB	R1, R2
   111  		//	MOV	$(framesize+(StackGuard-StackSmall)), R3
   112  		//	CMP	R3, R2
   113  		p = obj.Appendp(p, c.newprog)
   114  
   115  		p.As = ACMP
   116  		p.From.Type = obj.TYPE_CONST
   117  		p.From.Offset = objabi.StackPreempt
   118  		p.Reg = REG_R1
   119  
   120  		p = obj.Appendp(p, c.newprog)
   121  		q = p
   122  		p.As = ABEQ
   123  		p.To.Type = obj.TYPE_BRANCH
   124  
   125  		p = obj.Appendp(p, c.newprog)
   126  		p.As = AADD
   127  		p.From.Type = obj.TYPE_CONST
   128  		p.From.Offset = objabi.StackGuard
   129  		p.Reg = REGSP
   130  		p.To.Type = obj.TYPE_REG
   131  		p.To.Reg = REG_R2
   132  
   133  		p = obj.Appendp(p, c.newprog)
   134  		p.As = ASUB
   135  		p.From.Type = obj.TYPE_REG
   136  		p.From.Reg = REG_R1
   137  		p.To.Type = obj.TYPE_REG
   138  		p.To.Reg = REG_R2
   139  
   140  		p = obj.Appendp(p, c.newprog)
   141  		p.As = AMOVD
   142  		p.From.Type = obj.TYPE_CONST
   143  		p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
   144  		p.To.Type = obj.TYPE_REG
   145  		p.To.Reg = REG_R3
   146  
   147  		p = obj.Appendp(p, c.newprog)
   148  		p.As = ACMP
   149  		p.From.Type = obj.TYPE_REG
   150  		p.From.Reg = REG_R3
   151  		p.Reg = REG_R2
   152  	}
   153  
   154  	// BLS	do-morestack
   155  	bls := obj.Appendp(p, c.newprog)
   156  	bls.As = ABLS
   157  	bls.To.Type = obj.TYPE_BRANCH
   158  
   159  	var last *obj.Prog
   160  	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
   161  	}
   162  
   163  	// Now we are at the end of the function, but logically
   164  	// we are still in function prologue. We need to fix the
   165  	// SP data and PCDATA.
   166  	spfix := obj.Appendp(last, c.newprog)
   167  	spfix.As = obj.ANOP
   168  	spfix.Spadj = -framesize
   169  
   170  	pcdata := obj.Appendp(spfix, c.newprog)
   171  	pcdata.Pos = c.cursym.Func.Text.Pos
   172  	pcdata.As = obj.APCDATA
   173  	pcdata.From.Type = obj.TYPE_CONST
   174  	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
   175  	pcdata.To.Type = obj.TYPE_CONST
   176  	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
   177  
   178  	// MOV	LR, R3
   179  	movlr := obj.Appendp(pcdata, c.newprog)
   180  	movlr.As = AMOVD
   181  	movlr.From.Type = obj.TYPE_REG
   182  	movlr.From.Reg = REGLINK
   183  	movlr.To.Type = obj.TYPE_REG
   184  	movlr.To.Reg = REG_R3
   185  	if q != nil {
   186  		q.Pcond = movlr
   187  	}
   188  	bls.Pcond = movlr
   189  
   190  	debug := movlr
   191  	if false {
   192  		debug = obj.Appendp(debug, c.newprog)
   193  		debug.As = AMOVD
   194  		debug.From.Type = obj.TYPE_CONST
   195  		debug.From.Offset = int64(framesize)
   196  		debug.To.Type = obj.TYPE_REG
   197  		debug.To.Reg = REGTMP
   198  	}
   199  
   200  	// BL	runtime.morestack(SB)
   201  	call := obj.Appendp(debug, c.newprog)
   202  	call.As = ABL
   203  	call.To.Type = obj.TYPE_BRANCH
   204  	morestack := "runtime.morestack"
   205  	switch {
   206  	case c.cursym.CFunc():
   207  		morestack = "runtime.morestackc"
   208  	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
   209  		morestack = "runtime.morestack_noctxt"
   210  	}
   211  	call.To.Sym = c.ctxt.Lookup(morestack)
   212  
   213  	// B	start
   214  	jmp := obj.Appendp(call, c.newprog)
   215  	jmp.As = AB
   216  	jmp.To.Type = obj.TYPE_BRANCH
   217  	jmp.Pcond = c.cursym.Func.Text.Link
   218  	jmp.Spadj = +framesize
   219  
   220  	// placeholder for bls's jump target
   221  	// p = obj.Appendp(ctxt, p)
   222  	// p.As = obj.ANOP
   223  
   224  	return bls
   225  }
   226  
   227  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
   228  	c := ctxt7{ctxt: ctxt, newprog: newprog}
   229  
   230  	p.From.Class = 0
   231  	p.To.Class = 0
   232  
   233  	// $0 results in C_ZCON, which matches both C_REG and various
   234  	// C_xCON, however the C_REG cases in asmout don't expect a
   235  	// constant, so they will use the register fields and assemble
   236  	// a R0. To prevent that, rewrite $0 as ZR.
   237  	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
   238  		p.From.Type = obj.TYPE_REG
   239  		p.From.Reg = REGZERO
   240  	}
   241  	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
   242  		p.To.Type = obj.TYPE_REG
   243  		p.To.Reg = REGZERO
   244  	}
   245  
   246  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
   247  	switch p.As {
   248  	case AB,
   249  		ABL,
   250  		obj.ARET,
   251  		obj.ADUFFZERO,
   252  		obj.ADUFFCOPY:
   253  		if p.To.Sym != nil {
   254  			p.To.Type = obj.TYPE_BRANCH
   255  		}
   256  		break
   257  	}
   258  
   259  	// Rewrite float constants to values stored in memory.
   260  	switch p.As {
   261  	case AFMOVS:
   262  		if p.From.Type == obj.TYPE_FCONST {
   263  			f32 := float32(p.From.Val.(float64))
   264  			if math.Float32bits(f32) == 0 {
   265  				p.From.Type = obj.TYPE_REG
   266  				p.From.Reg = REGZERO
   267  				break
   268  			}
   269  			p.From.Type = obj.TYPE_MEM
   270  			p.From.Sym = c.ctxt.Float32Sym(f32)
   271  			p.From.Name = obj.NAME_EXTERN
   272  			p.From.Offset = 0
   273  		}
   274  
   275  	case AFMOVD:
   276  		if p.From.Type == obj.TYPE_FCONST {
   277  			f64 := p.From.Val.(float64)
   278  			if math.Float64bits(f64) == 0 {
   279  				p.From.Type = obj.TYPE_REG
   280  				p.From.Reg = REGZERO
   281  				break
   282  			}
   283  			p.From.Type = obj.TYPE_MEM
   284  			p.From.Sym = c.ctxt.Float64Sym(f64)
   285  			p.From.Name = obj.NAME_EXTERN
   286  			p.From.Offset = 0
   287  		}
   288  
   289  		break
   290  	}
   291  
   292  	// Rewrite negative immediates as positive immediates with
   293  	// complementary instruction.
   294  	switch p.As {
   295  	case AADD, ASUB, ACMP, ACMN:
   296  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
   297  			p.From.Offset = -p.From.Offset
   298  			p.As = complements[p.As]
   299  		}
   300  	case AADDW, ASUBW, ACMPW, ACMNW:
   301  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
   302  			p.From.Offset = -p.From.Offset
   303  			p.As = complements[p.As]
   304  		}
   305  	}
   306  
   307  	// For 32-bit logical instruction with constant,
   308  	// rewrite the high 32-bit to be a repetition of
   309  	// the low 32-bit, so that the BITCON test can be
   310  	// shared for both 32-bit and 64-bit. 32-bit ops
   311  	// will zero the high 32-bit of the destination
   312  	// register anyway.
   313  	switch p.As {
   314  	case AANDW, AORRW, AEORW, AANDSW:
   315  		if p.From.Type == obj.TYPE_CONST {
   316  			v := p.From.Offset & 0xffffffff
   317  			p.From.Offset = v | v<<32
   318  		}
   319  	}
   320  
   321  	if c.ctxt.Flag_dynlink {
   322  		c.rewriteToUseGot(p)
   323  	}
   324  }
   325  
   326  // Rewrite p, if necessary, to access global data via the global offset table.
   327  func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
   328  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   329  		//     ADUFFxxx $offset
   330  		// becomes
   331  		//     MOVD runtime.duffxxx@GOT, REGTMP
   332  		//     ADD $offset, REGTMP
   333  		//     CALL REGTMP
   334  		var sym *obj.LSym
   335  		if p.As == obj.ADUFFZERO {
   336  			sym = c.ctxt.Lookup("runtime.duffzero")
   337  		} else {
   338  			sym = c.ctxt.Lookup("runtime.duffcopy")
   339  		}
   340  		offset := p.To.Offset
   341  		p.As = AMOVD
   342  		p.From.Type = obj.TYPE_MEM
   343  		p.From.Name = obj.NAME_GOTREF
   344  		p.From.Sym = sym
   345  		p.To.Type = obj.TYPE_REG
   346  		p.To.Reg = REGTMP
   347  		p.To.Name = obj.NAME_NONE
   348  		p.To.Offset = 0
   349  		p.To.Sym = nil
   350  		p1 := obj.Appendp(p, c.newprog)
   351  		p1.As = AADD
   352  		p1.From.Type = obj.TYPE_CONST
   353  		p1.From.Offset = offset
   354  		p1.To.Type = obj.TYPE_REG
   355  		p1.To.Reg = REGTMP
   356  		p2 := obj.Appendp(p1, c.newprog)
   357  		p2.As = obj.ACALL
   358  		p2.To.Type = obj.TYPE_REG
   359  		p2.To.Reg = REGTMP
   360  	}
   361  
   362  	// We only care about global data: NAME_EXTERN means a global
   363  	// symbol in the Go sense, and p.Sym.Local is true for a few
   364  	// internally defined symbols.
   365  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   366  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   367  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   368  		if p.As != AMOVD {
   369  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   370  		}
   371  		if p.To.Type != obj.TYPE_REG {
   372  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   373  		}
   374  		p.From.Type = obj.TYPE_MEM
   375  		p.From.Name = obj.NAME_GOTREF
   376  		if p.From.Offset != 0 {
   377  			q := obj.Appendp(p, c.newprog)
   378  			q.As = AADD
   379  			q.From.Type = obj.TYPE_CONST
   380  			q.From.Offset = p.From.Offset
   381  			q.To = p.To
   382  			p.From.Offset = 0
   383  		}
   384  	}
   385  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   386  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   387  	}
   388  	var source *obj.Addr
   389  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   390  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   391  	// An addition may be inserted between the two MOVs if there is an offset.
   392  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   393  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   394  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   395  		}
   396  		source = &p.From
   397  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   398  		source = &p.To
   399  	} else {
   400  		return
   401  	}
   402  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   403  		return
   404  	}
   405  	if source.Sym.Type == objabi.STLSBSS {
   406  		return
   407  	}
   408  	if source.Type != obj.TYPE_MEM {
   409  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   410  	}
   411  	p1 := obj.Appendp(p, c.newprog)
   412  	p2 := obj.Appendp(p1, c.newprog)
   413  	p1.As = AMOVD
   414  	p1.From.Type = obj.TYPE_MEM
   415  	p1.From.Sym = source.Sym
   416  	p1.From.Name = obj.NAME_GOTREF
   417  	p1.To.Type = obj.TYPE_REG
   418  	p1.To.Reg = REGTMP
   419  
   420  	p2.As = p.As
   421  	p2.From = p.From
   422  	p2.To = p.To
   423  	if p.From.Name == obj.NAME_EXTERN {
   424  		p2.From.Reg = REGTMP
   425  		p2.From.Name = obj.NAME_NONE
   426  		p2.From.Sym = nil
   427  	} else if p.To.Name == obj.NAME_EXTERN {
   428  		p2.To.Reg = REGTMP
   429  		p2.To.Name = obj.NAME_NONE
   430  		p2.To.Sym = nil
   431  	} else {
   432  		return
   433  	}
   434  	obj.Nopout(p)
   435  }
   436  
   437  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   438  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   439  		return
   440  	}
   441  
   442  	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
   443  
   444  	p := c.cursym.Func.Text
   445  	textstksiz := p.To.Offset
   446  	aoffset := int32(textstksiz)
   447  
   448  	c.cursym.Func.Args = p.To.Val.(int32)
   449  	c.cursym.Func.Locals = int32(textstksiz)
   450  
   451  	/*
   452  	 * find leaf subroutines
   453  	 * strip NOPs
   454  	 * expand RET
   455  	 */
   456  	q := (*obj.Prog)(nil)
   457  	var q1 *obj.Prog
   458  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   459  		switch p.As {
   460  		case obj.ATEXT:
   461  			p.Mark |= LEAF
   462  
   463  		case obj.ARET:
   464  			break
   465  
   466  		case obj.ANOP:
   467  			q1 = p.Link
   468  			q.Link = q1 /* q is non-nop */
   469  			q1.Mark |= p.Mark
   470  			continue
   471  
   472  		case ABL,
   473  			obj.ADUFFZERO,
   474  			obj.ADUFFCOPY:
   475  			c.cursym.Func.Text.Mark &^= LEAF
   476  			fallthrough
   477  
   478  		case ACBNZ,
   479  			ACBZ,
   480  			ACBNZW,
   481  			ACBZW,
   482  			ATBZ,
   483  			ATBNZ,
   484  			AB,
   485  			ABEQ,
   486  			ABNE,
   487  			ABCS,
   488  			ABHS,
   489  			ABCC,
   490  			ABLO,
   491  			ABMI,
   492  			ABPL,
   493  			ABVS,
   494  			ABVC,
   495  			ABHI,
   496  			ABLS,
   497  			ABGE,
   498  			ABLT,
   499  			ABGT,
   500  			ABLE,
   501  			AADR, /* strange */
   502  			AADRP:
   503  			q1 = p.Pcond
   504  
   505  			if q1 != nil {
   506  				for q1.As == obj.ANOP {
   507  					q1 = q1.Link
   508  					p.Pcond = q1
   509  				}
   510  			}
   511  
   512  			break
   513  		}
   514  
   515  		q = p
   516  	}
   517  
   518  	var q2 *obj.Prog
   519  	var retjmp *obj.LSym
   520  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   521  		o := p.As
   522  		switch o {
   523  		case obj.ATEXT:
   524  			c.cursym.Func.Text = p
   525  			if textstksiz < 0 {
   526  				c.autosize = 0
   527  			} else {
   528  				c.autosize = int32(textstksiz + 8)
   529  			}
   530  			if (c.cursym.Func.Text.Mark&LEAF != 0) && c.autosize <= 8 {
   531  				c.autosize = 0
   532  			} else if c.autosize&(16-1) != 0 {
   533  				// The frame includes an LR.
   534  				// If the frame size is 8, it's only an LR,
   535  				// so there's no potential for breaking references to
   536  				// local variables by growing the frame size,
   537  				// because there are no local variables.
   538  				// But otherwise, if there is a non-empty locals section,
   539  				// the author of the code is responsible for making sure
   540  				// that the frame size is 8 mod 16.
   541  				if c.autosize == 8 {
   542  					c.autosize += 8
   543  					c.cursym.Func.Locals += 8
   544  				} else {
   545  					c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
   546  				}
   547  			}
   548  			p.To.Offset = int64(c.autosize) - 8
   549  			if c.autosize == 0 && !(c.cursym.Func.Text.Mark&LEAF != 0) {
   550  				if c.ctxt.Debugvlog {
   551  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
   552  				}
   553  				c.cursym.Func.Text.Mark |= LEAF
   554  			}
   555  
   556  			if !p.From.Sym.NoSplit() {
   557  				p = c.stacksplit(p, c.autosize) // emit split check
   558  			}
   559  
   560  			aoffset = c.autosize
   561  			if aoffset > 0xF0 {
   562  				aoffset = 0xF0
   563  			}
   564  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   565  				c.cursym.Set(obj.AttrLeaf, true)
   566  				if c.autosize == 0 {
   567  					break
   568  				}
   569  			}
   570  
   571  			// Frame is non-empty. Make sure to save link register, even if
   572  			// it is a leaf function, so that traceback works.
   573  			q = p
   574  			if c.autosize > aoffset {
   575  				// Frame size is too large for a MOVD.W instruction.
   576  				// Store link register before decrementing SP, so if a signal comes
   577  				// during the execution of the function prologue, the traceback
   578  				// code will not see a half-updated stack frame.
   579  				q = obj.Appendp(q, c.newprog)
   580  				q.Pos = p.Pos
   581  				q.As = ASUB
   582  				q.From.Type = obj.TYPE_CONST
   583  				q.From.Offset = int64(c.autosize)
   584  				q.Reg = REGSP
   585  				q.To.Type = obj.TYPE_REG
   586  				q.To.Reg = REGTMP
   587  
   588  				q = obj.Appendp(q, c.newprog)
   589  				q.Pos = p.Pos
   590  				q.As = AMOVD
   591  				q.From.Type = obj.TYPE_REG
   592  				q.From.Reg = REGLINK
   593  				q.To.Type = obj.TYPE_MEM
   594  				q.To.Reg = REGTMP
   595  
   596  				q1 = obj.Appendp(q, c.newprog)
   597  				q1.Pos = p.Pos
   598  				q1.As = AMOVD
   599  				q1.From.Type = obj.TYPE_REG
   600  				q1.From.Reg = REGTMP
   601  				q1.To.Type = obj.TYPE_REG
   602  				q1.To.Reg = REGSP
   603  				q1.Spadj = c.autosize
   604  			} else {
   605  				// small frame, update SP and save LR in a single MOVD.W instruction
   606  				q1 = obj.Appendp(q, c.newprog)
   607  				q1.As = AMOVD
   608  				q1.Pos = p.Pos
   609  				q1.From.Type = obj.TYPE_REG
   610  				q1.From.Reg = REGLINK
   611  				q1.To.Type = obj.TYPE_MEM
   612  				q1.Scond = C_XPRE
   613  				q1.To.Offset = int64(-aoffset)
   614  				q1.To.Reg = REGSP
   615  				q1.Spadj = aoffset
   616  			}
   617  
   618  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   619  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   620  				//
   621  				//	MOV g_panic(g), R1
   622  				//	CMP ZR, R1
   623  				//	BEQ end
   624  				//	MOV panic_argp(R1), R2
   625  				//	ADD $(autosize+8), RSP, R3
   626  				//	CMP R2, R3
   627  				//	BNE end
   628  				//	ADD $8, RSP, R4
   629  				//	MOVD R4, panic_argp(R1)
   630  				// end:
   631  				//	NOP
   632  				//
   633  				// The NOP is needed to give the jumps somewhere to land.
   634  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   635  				q = q1
   636  
   637  				q = obj.Appendp(q, c.newprog)
   638  				q.As = AMOVD
   639  				q.From.Type = obj.TYPE_MEM
   640  				q.From.Reg = REGG
   641  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   642  				q.To.Type = obj.TYPE_REG
   643  				q.To.Reg = REG_R1
   644  
   645  				q = obj.Appendp(q, c.newprog)
   646  				q.As = ACMP
   647  				q.From.Type = obj.TYPE_REG
   648  				q.From.Reg = REGZERO
   649  				q.Reg = REG_R1
   650  
   651  				q = obj.Appendp(q, c.newprog)
   652  				q.As = ABEQ
   653  				q.To.Type = obj.TYPE_BRANCH
   654  				q1 = q
   655  
   656  				q = obj.Appendp(q, c.newprog)
   657  				q.As = AMOVD
   658  				q.From.Type = obj.TYPE_MEM
   659  				q.From.Reg = REG_R1
   660  				q.From.Offset = 0 // Panic.argp
   661  				q.To.Type = obj.TYPE_REG
   662  				q.To.Reg = REG_R2
   663  
   664  				q = obj.Appendp(q, c.newprog)
   665  				q.As = AADD
   666  				q.From.Type = obj.TYPE_CONST
   667  				q.From.Offset = int64(c.autosize) + 8
   668  				q.Reg = REGSP
   669  				q.To.Type = obj.TYPE_REG
   670  				q.To.Reg = REG_R3
   671  
   672  				q = obj.Appendp(q, c.newprog)
   673  				q.As = ACMP
   674  				q.From.Type = obj.TYPE_REG
   675  				q.From.Reg = REG_R2
   676  				q.Reg = REG_R3
   677  
   678  				q = obj.Appendp(q, c.newprog)
   679  				q.As = ABNE
   680  				q.To.Type = obj.TYPE_BRANCH
   681  				q2 = q
   682  
   683  				q = obj.Appendp(q, c.newprog)
   684  				q.As = AADD
   685  				q.From.Type = obj.TYPE_CONST
   686  				q.From.Offset = 8
   687  				q.Reg = REGSP
   688  				q.To.Type = obj.TYPE_REG
   689  				q.To.Reg = REG_R4
   690  
   691  				q = obj.Appendp(q, c.newprog)
   692  				q.As = AMOVD
   693  				q.From.Type = obj.TYPE_REG
   694  				q.From.Reg = REG_R4
   695  				q.To.Type = obj.TYPE_MEM
   696  				q.To.Reg = REG_R1
   697  				q.To.Offset = 0 // Panic.argp
   698  
   699  				q = obj.Appendp(q, c.newprog)
   700  
   701  				q.As = obj.ANOP
   702  				q1.Pcond = q
   703  				q2.Pcond = q
   704  			}
   705  
   706  		case obj.ARET:
   707  			nocache(p)
   708  			if p.From.Type == obj.TYPE_CONST {
   709  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   710  				break
   711  			}
   712  
   713  			retjmp = p.To.Sym
   714  			p.To = obj.Addr{}
   715  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   716  				if c.autosize != 0 {
   717  					p.As = AADD
   718  					p.From.Type = obj.TYPE_CONST
   719  					p.From.Offset = int64(c.autosize)
   720  					p.To.Type = obj.TYPE_REG
   721  					p.To.Reg = REGSP
   722  					p.Spadj = -c.autosize
   723  				}
   724  			} else {
   725  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   726  				aoffset = c.autosize
   727  
   728  				if aoffset > 0xF0 {
   729  					aoffset = 0xF0
   730  				}
   731  				p.As = AMOVD
   732  				p.From.Type = obj.TYPE_MEM
   733  				p.Scond = C_XPOST
   734  				p.From.Offset = int64(aoffset)
   735  				p.From.Reg = REGSP
   736  				p.To.Type = obj.TYPE_REG
   737  				p.To.Reg = REGLINK
   738  				p.Spadj = -aoffset
   739  				if c.autosize > aoffset {
   740  					q = newprog()
   741  					q.As = AADD
   742  					q.From.Type = obj.TYPE_CONST
   743  					q.From.Offset = int64(c.autosize) - int64(aoffset)
   744  					q.To.Type = obj.TYPE_REG
   745  					q.To.Reg = REGSP
   746  					q.Link = p.Link
   747  					q.Spadj = int32(-q.From.Offset)
   748  					q.Pos = p.Pos
   749  					p.Link = q
   750  					p = q
   751  				}
   752  			}
   753  
   754  			if p.As != obj.ARET {
   755  				q = newprog()
   756  				q.Pos = p.Pos
   757  				q.Link = p.Link
   758  				p.Link = q
   759  				p = q
   760  			}
   761  
   762  			if retjmp != nil { // retjmp
   763  				p.As = AB
   764  				p.To.Type = obj.TYPE_BRANCH
   765  				p.To.Sym = retjmp
   766  				p.Spadj = +c.autosize
   767  				break
   768  			}
   769  
   770  			p.As = obj.ARET
   771  			p.To.Type = obj.TYPE_MEM
   772  			p.To.Offset = 0
   773  			p.To.Reg = REGLINK
   774  			p.Spadj = +c.autosize
   775  
   776  		case AADD, ASUB:
   777  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   778  				if p.As == AADD {
   779  					p.Spadj = int32(-p.From.Offset)
   780  				} else {
   781  					p.Spadj = int32(+p.From.Offset)
   782  				}
   783  			}
   784  			break
   785  		}
   786  	}
   787  }
   788  
   789  func nocache(p *obj.Prog) {
   790  	p.Optab = 0
   791  	p.From.Class = 0
   792  	p.To.Class = 0
   793  }
   794  
   795  var unaryDst = map[obj.As]bool{
   796  	AWORD:  true,
   797  	ADWORD: true,
   798  	ABL:    true,
   799  	AB:     true,
   800  	ASVC:   true,
   801  }
   802  
   803  var Linkarm64 = obj.LinkArch{
   804  	Arch:       sys.ArchARM64,
   805  	Init:       buildop,
   806  	Preprocess: preprocess,
   807  	Assemble:   span7,
   808  	Progedit:   progedit,
   809  	UnaryDst:   unaryDst,
   810  }