github.com/goproxy0/go@v0.0.0-20171111080102-49cc0c489d2c/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.GetFrom3() != nil && p.GetFrom3().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 retjmp *obj.LSym
   519  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   520  		o := p.As
   521  		switch o {
   522  		case obj.ATEXT:
   523  			c.cursym.Func.Text = p
   524  			if textstksiz < 0 {
   525  				c.autosize = 0
   526  			} else {
   527  				c.autosize = int32(textstksiz + 8)
   528  			}
   529  			if (c.cursym.Func.Text.Mark&LEAF != 0) && c.autosize <= 8 {
   530  				c.autosize = 0
   531  			} else if c.autosize&(16-1) != 0 {
   532  				// The frame includes an LR.
   533  				// If the frame size is 8, it's only an LR,
   534  				// so there's no potential for breaking references to
   535  				// local variables by growing the frame size,
   536  				// because there are no local variables.
   537  				// But otherwise, if there is a non-empty locals section,
   538  				// the author of the code is responsible for making sure
   539  				// that the frame size is 8 mod 16.
   540  				if c.autosize == 8 {
   541  					c.autosize += 8
   542  					c.cursym.Func.Locals += 8
   543  				} else {
   544  					c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
   545  				}
   546  			}
   547  			p.To.Offset = int64(c.autosize) - 8
   548  			if c.autosize == 0 && !(c.cursym.Func.Text.Mark&LEAF != 0) {
   549  				if c.ctxt.Debugvlog {
   550  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
   551  				}
   552  				c.cursym.Func.Text.Mark |= LEAF
   553  			}
   554  
   555  			if !p.From.Sym.NoSplit() {
   556  				p = c.stacksplit(p, c.autosize) // emit split check
   557  			}
   558  
   559  			aoffset = c.autosize
   560  			if aoffset > 0xF0 {
   561  				aoffset = 0xF0
   562  			}
   563  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   564  				c.cursym.Set(obj.AttrLeaf, true)
   565  				if c.autosize == 0 {
   566  					break
   567  				}
   568  			}
   569  
   570  			// Frame is non-empty. Make sure to save link register, even if
   571  			// it is a leaf function, so that traceback works.
   572  			q = p
   573  			if c.autosize > aoffset {
   574  				// Frame size is too large for a MOVD.W instruction.
   575  				// Store link register before decrementing SP, so if a signal comes
   576  				// during the execution of the function prologue, the traceback
   577  				// code will not see a half-updated stack frame.
   578  				q = obj.Appendp(q, c.newprog)
   579  				q.Pos = p.Pos
   580  				q.As = ASUB
   581  				q.From.Type = obj.TYPE_CONST
   582  				q.From.Offset = int64(c.autosize)
   583  				q.Reg = REGSP
   584  				q.To.Type = obj.TYPE_REG
   585  				q.To.Reg = REGTMP
   586  
   587  				q = obj.Appendp(q, c.newprog)
   588  				q.Pos = p.Pos
   589  				q.As = AMOVD
   590  				q.From.Type = obj.TYPE_REG
   591  				q.From.Reg = REGLINK
   592  				q.To.Type = obj.TYPE_MEM
   593  				q.To.Reg = REGTMP
   594  
   595  				q1 = obj.Appendp(q, c.newprog)
   596  				q1.Pos = p.Pos
   597  				q1.As = AMOVD
   598  				q1.From.Type = obj.TYPE_REG
   599  				q1.From.Reg = REGTMP
   600  				q1.To.Type = obj.TYPE_REG
   601  				q1.To.Reg = REGSP
   602  				q1.Spadj = c.autosize
   603  			} else {
   604  				// small frame, update SP and save LR in a single MOVD.W instruction
   605  				q1 = obj.Appendp(q, c.newprog)
   606  				q1.As = AMOVD
   607  				q1.Pos = p.Pos
   608  				q1.From.Type = obj.TYPE_REG
   609  				q1.From.Reg = REGLINK
   610  				q1.To.Type = obj.TYPE_MEM
   611  				q1.Scond = C_XPRE
   612  				q1.To.Offset = int64(-aoffset)
   613  				q1.To.Reg = REGSP
   614  				q1.Spadj = aoffset
   615  			}
   616  
   617  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   618  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   619  				//
   620  				//	MOV  g_panic(g), R1
   621  				//	CBNZ checkargp
   622  				// end:
   623  				//	NOP
   624  				// ... function body ...
   625  				// checkargp:
   626  				//	MOV  panic_argp(R1), R2
   627  				//	ADD  $(autosize+8), RSP, R3
   628  				//	CMP  R2, R3
   629  				//	BNE  end
   630  				//	ADD  $8, RSP, R4
   631  				//	MOVD R4, panic_argp(R1)
   632  				//	B    end
   633  				//
   634  				// The NOP is needed to give the jumps somewhere to land.
   635  				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
   636  				q = q1
   637  
   638  				// MOV g_panic(g), R1
   639  				q = obj.Appendp(q, c.newprog)
   640  				q.As = AMOVD
   641  				q.From.Type = obj.TYPE_MEM
   642  				q.From.Reg = REGG
   643  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   644  				q.To.Type = obj.TYPE_REG
   645  				q.To.Reg = REG_R1
   646  
   647  				// CBNZ R1, checkargp
   648  				cbnz := obj.Appendp(q, c.newprog)
   649  				cbnz.As = ACBNZ
   650  				cbnz.From.Type = obj.TYPE_REG
   651  				cbnz.From.Reg = REG_R1
   652  				cbnz.To.Type = obj.TYPE_BRANCH
   653  
   654  				// Empty branch target at the top of the function body
   655  				end := obj.Appendp(cbnz, c.newprog)
   656  				end.As = obj.ANOP
   657  
   658  				// find the end of the function
   659  				var last *obj.Prog
   660  				for last = end; last.Link != nil; last = last.Link {
   661  				}
   662  
   663  				// MOV panic_argp(R1), R2
   664  				mov := obj.Appendp(last, c.newprog)
   665  				mov.As = AMOVD
   666  				mov.From.Type = obj.TYPE_MEM
   667  				mov.From.Reg = REG_R1
   668  				mov.From.Offset = 0 // Panic.argp
   669  				mov.To.Type = obj.TYPE_REG
   670  				mov.To.Reg = REG_R2
   671  
   672  				// CBNZ branches to the MOV above
   673  				cbnz.Pcond = mov
   674  
   675  				// ADD $(autosize+8), SP, R3
   676  				q = obj.Appendp(mov, c.newprog)
   677  				q.As = AADD
   678  				q.From.Type = obj.TYPE_CONST
   679  				q.From.Offset = int64(c.autosize) + 8
   680  				q.Reg = REGSP
   681  				q.To.Type = obj.TYPE_REG
   682  				q.To.Reg = REG_R3
   683  
   684  				// CMP R2, R3
   685  				q = obj.Appendp(q, c.newprog)
   686  				q.As = ACMP
   687  				q.From.Type = obj.TYPE_REG
   688  				q.From.Reg = REG_R2
   689  				q.Reg = REG_R3
   690  
   691  				// BNE end
   692  				q = obj.Appendp(q, c.newprog)
   693  				q.As = ABNE
   694  				q.To.Type = obj.TYPE_BRANCH
   695  				q.Pcond = end
   696  
   697  				// ADD $8, SP, R4
   698  				q = obj.Appendp(q, c.newprog)
   699  				q.As = AADD
   700  				q.From.Type = obj.TYPE_CONST
   701  				q.From.Offset = 8
   702  				q.Reg = REGSP
   703  				q.To.Type = obj.TYPE_REG
   704  				q.To.Reg = REG_R4
   705  
   706  				// MOV R4, panic_argp(R1)
   707  				q = obj.Appendp(q, c.newprog)
   708  				q.As = AMOVD
   709  				q.From.Type = obj.TYPE_REG
   710  				q.From.Reg = REG_R4
   711  				q.To.Type = obj.TYPE_MEM
   712  				q.To.Reg = REG_R1
   713  				q.To.Offset = 0 // Panic.argp
   714  
   715  				// B end
   716  				q = obj.Appendp(q, c.newprog)
   717  				q.As = AB
   718  				q.To.Type = obj.TYPE_BRANCH
   719  				q.Pcond = end
   720  			}
   721  
   722  		case obj.ARET:
   723  			nocache(p)
   724  			if p.From.Type == obj.TYPE_CONST {
   725  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   726  				break
   727  			}
   728  
   729  			retjmp = p.To.Sym
   730  			p.To = obj.Addr{}
   731  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   732  				if c.autosize != 0 {
   733  					p.As = AADD
   734  					p.From.Type = obj.TYPE_CONST
   735  					p.From.Offset = int64(c.autosize)
   736  					p.To.Type = obj.TYPE_REG
   737  					p.To.Reg = REGSP
   738  					p.Spadj = -c.autosize
   739  				}
   740  			} else {
   741  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   742  				aoffset = c.autosize
   743  
   744  				if aoffset > 0xF0 {
   745  					aoffset = 0xF0
   746  				}
   747  				p.As = AMOVD
   748  				p.From.Type = obj.TYPE_MEM
   749  				p.Scond = C_XPOST
   750  				p.From.Offset = int64(aoffset)
   751  				p.From.Reg = REGSP
   752  				p.To.Type = obj.TYPE_REG
   753  				p.To.Reg = REGLINK
   754  				p.Spadj = -aoffset
   755  				if c.autosize > aoffset {
   756  					q = newprog()
   757  					q.As = AADD
   758  					q.From.Type = obj.TYPE_CONST
   759  					q.From.Offset = int64(c.autosize) - int64(aoffset)
   760  					q.To.Type = obj.TYPE_REG
   761  					q.To.Reg = REGSP
   762  					q.Link = p.Link
   763  					q.Spadj = int32(-q.From.Offset)
   764  					q.Pos = p.Pos
   765  					p.Link = q
   766  					p = q
   767  				}
   768  			}
   769  
   770  			if p.As != obj.ARET {
   771  				q = newprog()
   772  				q.Pos = p.Pos
   773  				q.Link = p.Link
   774  				p.Link = q
   775  				p = q
   776  			}
   777  
   778  			if retjmp != nil { // retjmp
   779  				p.As = AB
   780  				p.To.Type = obj.TYPE_BRANCH
   781  				p.To.Sym = retjmp
   782  				p.Spadj = +c.autosize
   783  				break
   784  			}
   785  
   786  			p.As = obj.ARET
   787  			p.To.Type = obj.TYPE_MEM
   788  			p.To.Offset = 0
   789  			p.To.Reg = REGLINK
   790  			p.Spadj = +c.autosize
   791  
   792  		case AADD, ASUB:
   793  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   794  				if p.As == AADD {
   795  					p.Spadj = int32(-p.From.Offset)
   796  				} else {
   797  					p.Spadj = int32(+p.From.Offset)
   798  				}
   799  			}
   800  			break
   801  		}
   802  	}
   803  }
   804  
   805  func nocache(p *obj.Prog) {
   806  	p.Optab = 0
   807  	p.From.Class = 0
   808  	p.To.Class = 0
   809  }
   810  
   811  var unaryDst = map[obj.As]bool{
   812  	AWORD:  true,
   813  	ADWORD: true,
   814  	ABL:    true,
   815  	AB:     true,
   816  	ASVC:   true,
   817  }
   818  
   819  var Linkarm64 = obj.LinkArch{
   820  	Arch:       sys.ArchARM64,
   821  	Init:       buildop,
   822  	Preprocess: preprocess,
   823  	Assemble:   span7,
   824  	Progedit:   progedit,
   825  	UnaryDst:   unaryDst,
   826  }