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