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