github.com/zxy12/go_duplicate_1_12@v0.0.0-20200217043740-b1636fc0368b/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 = int64(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) + (int64(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 := c.ctxt.EmitEntryLiveness(c.cursym, spfix, c.newprog)
   171  
   172  	// MOV  LR, R3
   173  	movlr := obj.Appendp(pcdata, c.newprog)
   174  	movlr.As = AMOVD
   175  	movlr.From.Type = obj.TYPE_REG
   176  	movlr.From.Reg = REGLINK
   177  	movlr.To.Type = obj.TYPE_REG
   178  	movlr.To.Reg = REG_R3
   179  	if q != nil {
   180  		q.Pcond = movlr
   181  	}
   182  	bls.Pcond = movlr
   183  
   184  	debug := movlr
   185  	if false {
   186  		debug = obj.Appendp(debug, c.newprog)
   187  		debug.As = AMOVD
   188  		debug.From.Type = obj.TYPE_CONST
   189  		debug.From.Offset = int64(framesize)
   190  		debug.To.Type = obj.TYPE_REG
   191  		debug.To.Reg = REGTMP
   192  	}
   193  
   194  	// BL   runtime.morestack(SB)
   195  	call := obj.Appendp(debug, c.newprog)
   196  	call.As = ABL
   197  	call.To.Type = obj.TYPE_BRANCH
   198  	morestack := "runtime.morestack"
   199  	switch {
   200  	case c.cursym.CFunc():
   201  		morestack = "runtime.morestackc"
   202  	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
   203  		morestack = "runtime.morestack_noctxt"
   204  	}
   205  	call.To.Sym = c.ctxt.Lookup(morestack)
   206  
   207  	// B    start
   208  	jmp := obj.Appendp(call, c.newprog)
   209  	jmp.As = AB
   210  	jmp.To.Type = obj.TYPE_BRANCH
   211  	jmp.Pcond = c.cursym.Func.Text.Link
   212  	jmp.Spadj = +framesize
   213  
   214  	// placeholder for bls's jump target
   215  	// p = obj.Appendp(ctxt, p)
   216  	// p.As = obj.ANOP
   217  
   218  	return bls
   219  }
   220  
   221  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
   222  	c := ctxt7{ctxt: ctxt, newprog: newprog}
   223  
   224  	p.From.Class = 0
   225  	p.To.Class = 0
   226  
   227  	// $0 results in C_ZCON, which matches both C_REG and various
   228  	// C_xCON, however the C_REG cases in asmout don't expect a
   229  	// constant, so they will use the register fields and assemble
   230  	// a R0. To prevent that, rewrite $0 as ZR.
   231  	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
   232  		p.From.Type = obj.TYPE_REG
   233  		p.From.Reg = REGZERO
   234  	}
   235  	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
   236  		p.To.Type = obj.TYPE_REG
   237  		p.To.Reg = REGZERO
   238  	}
   239  
   240  	// Rewrite BR/BL to symbol as TYPE_BRANCH.
   241  	switch p.As {
   242  	case AB,
   243  		ABL,
   244  		obj.ARET,
   245  		obj.ADUFFZERO,
   246  		obj.ADUFFCOPY:
   247  		if p.To.Sym != nil {
   248  			p.To.Type = obj.TYPE_BRANCH
   249  		}
   250  		break
   251  	}
   252  
   253  	// Rewrite float constants to values stored in memory.
   254  	switch p.As {
   255  	case AFMOVS:
   256  		if p.From.Type == obj.TYPE_FCONST {
   257  			f64 := p.From.Val.(float64)
   258  			f32 := float32(f64)
   259  			if c.chipfloat7(f64) > 0 {
   260  				break
   261  			}
   262  			if math.Float32bits(f32) == 0 {
   263  				p.From.Type = obj.TYPE_REG
   264  				p.From.Reg = REGZERO
   265  				break
   266  			}
   267  			p.From.Type = obj.TYPE_MEM
   268  			p.From.Sym = c.ctxt.Float32Sym(f32)
   269  			p.From.Name = obj.NAME_EXTERN
   270  			p.From.Offset = 0
   271  		}
   272  
   273  	case AFMOVD:
   274  		if p.From.Type == obj.TYPE_FCONST {
   275  			f64 := p.From.Val.(float64)
   276  			if c.chipfloat7(f64) > 0 {
   277  				break
   278  			}
   279  			if math.Float64bits(f64) == 0 {
   280  				p.From.Type = obj.TYPE_REG
   281  				p.From.Reg = REGZERO
   282  				break
   283  			}
   284  			p.From.Type = obj.TYPE_MEM
   285  			p.From.Sym = c.ctxt.Float64Sym(f64)
   286  			p.From.Name = obj.NAME_EXTERN
   287  			p.From.Offset = 0
   288  		}
   289  
   290  		break
   291  	}
   292  
   293  	// Rewrite negative immediates as positive immediates with
   294  	// complementary instruction.
   295  	switch p.As {
   296  	case AADD, ASUB, ACMP, ACMN:
   297  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
   298  			p.From.Offset = -p.From.Offset
   299  			p.As = complements[p.As]
   300  		}
   301  	case AADDW, ASUBW, ACMPW, ACMNW:
   302  		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
   303  			p.From.Offset = -p.From.Offset
   304  			p.As = complements[p.As]
   305  		}
   306  	}
   307  
   308  	// For 32-bit logical instruction with constant,
   309  	// rewrite the high 32-bit to be a repetition of
   310  	// the low 32-bit, so that the BITCON test can be
   311  	// shared for both 32-bit and 64-bit. 32-bit ops
   312  	// will zero the high 32-bit of the destination
   313  	// register anyway.
   314  	if isANDWop(p.As) && p.From.Type == obj.TYPE_CONST {
   315  		v := p.From.Offset & 0xffffffff
   316  		p.From.Offset = v | v<<32
   317  	}
   318  
   319  	if c.ctxt.Flag_dynlink {
   320  		c.rewriteToUseGot(p)
   321  	}
   322  }
   323  
   324  // Rewrite p, if necessary, to access global data via the global offset table.
   325  func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
   326  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   327  		//     ADUFFxxx $offset
   328  		// becomes
   329  		//     MOVD runtime.duffxxx@GOT, REGTMP
   330  		//     ADD $offset, REGTMP
   331  		//     CALL REGTMP
   332  		var sym *obj.LSym
   333  		if p.As == obj.ADUFFZERO {
   334  			sym = c.ctxt.Lookup("runtime.duffzero")
   335  		} else {
   336  			sym = c.ctxt.Lookup("runtime.duffcopy")
   337  		}
   338  		offset := p.To.Offset
   339  		p.As = AMOVD
   340  		p.From.Type = obj.TYPE_MEM
   341  		p.From.Name = obj.NAME_GOTREF
   342  		p.From.Sym = sym
   343  		p.To.Type = obj.TYPE_REG
   344  		p.To.Reg = REGTMP
   345  		p.To.Name = obj.NAME_NONE
   346  		p.To.Offset = 0
   347  		p.To.Sym = nil
   348  		p1 := obj.Appendp(p, c.newprog)
   349  		p1.As = AADD
   350  		p1.From.Type = obj.TYPE_CONST
   351  		p1.From.Offset = offset
   352  		p1.To.Type = obj.TYPE_REG
   353  		p1.To.Reg = REGTMP
   354  		p2 := obj.Appendp(p1, c.newprog)
   355  		p2.As = obj.ACALL
   356  		p2.To.Type = obj.TYPE_REG
   357  		p2.To.Reg = REGTMP
   358  	}
   359  
   360  	// We only care about global data: NAME_EXTERN means a global
   361  	// symbol in the Go sense, and p.Sym.Local is true for a few
   362  	// internally defined symbols.
   363  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   364  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   365  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   366  		if p.As != AMOVD {
   367  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   368  		}
   369  		if p.To.Type != obj.TYPE_REG {
   370  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   371  		}
   372  		p.From.Type = obj.TYPE_MEM
   373  		p.From.Name = obj.NAME_GOTREF
   374  		if p.From.Offset != 0 {
   375  			q := obj.Appendp(p, c.newprog)
   376  			q.As = AADD
   377  			q.From.Type = obj.TYPE_CONST
   378  			q.From.Offset = p.From.Offset
   379  			q.To = p.To
   380  			p.From.Offset = 0
   381  		}
   382  	}
   383  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   384  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   385  	}
   386  	var source *obj.Addr
   387  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   388  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   389  	// An addition may be inserted between the two MOVs if there is an offset.
   390  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   391  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   392  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   393  		}
   394  		source = &p.From
   395  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   396  		source = &p.To
   397  	} else {
   398  		return
   399  	}
   400  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   401  		return
   402  	}
   403  	if source.Sym.Type == objabi.STLSBSS {
   404  		return
   405  	}
   406  	if source.Type != obj.TYPE_MEM {
   407  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   408  	}
   409  	p1 := obj.Appendp(p, c.newprog)
   410  	p2 := obj.Appendp(p1, c.newprog)
   411  	p1.As = AMOVD
   412  	p1.From.Type = obj.TYPE_MEM
   413  	p1.From.Sym = source.Sym
   414  	p1.From.Name = obj.NAME_GOTREF
   415  	p1.To.Type = obj.TYPE_REG
   416  	p1.To.Reg = REGTMP
   417  
   418  	p2.As = p.As
   419  	p2.From = p.From
   420  	p2.To = p.To
   421  	if p.From.Name == obj.NAME_EXTERN {
   422  		p2.From.Reg = REGTMP
   423  		p2.From.Name = obj.NAME_NONE
   424  		p2.From.Sym = nil
   425  	} else if p.To.Name == obj.NAME_EXTERN {
   426  		p2.To.Reg = REGTMP
   427  		p2.To.Name = obj.NAME_NONE
   428  		p2.To.Sym = nil
   429  	} else {
   430  		return
   431  	}
   432  	obj.Nopout(p)
   433  }
   434  
   435  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   436  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   437  		return
   438  	}
   439  
   440  	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
   441  
   442  	p := c.cursym.Func.Text
   443  	textstksiz := p.To.Offset
   444  	if textstksiz == -8 {
   445  		// Historical way to mark NOFRAME.
   446  		p.From.Sym.Set(obj.AttrNoFrame, true)
   447  		textstksiz = 0
   448  	}
   449  	if textstksiz < 0 {
   450  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
   451  	}
   452  	if p.From.Sym.NoFrame() {
   453  		if textstksiz != 0 {
   454  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   455  		}
   456  	}
   457  
   458  	c.cursym.Func.Args = p.To.Val.(int32)
   459  	c.cursym.Func.Locals = int32(textstksiz)
   460  
   461  	/*
   462  	 * find leaf subroutines
   463  	 * strip NOPs
   464  	 * expand RET
   465  	 */
   466  	q := (*obj.Prog)(nil)
   467  	var q1 *obj.Prog
   468  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   469  		switch p.As {
   470  		case obj.ATEXT:
   471  			p.Mark |= LEAF
   472  
   473  		case obj.ARET:
   474  			break
   475  
   476  		case obj.ANOP:
   477  			if p.Link != nil {
   478  				q1 = p.Link
   479  				q.Link = q1 /* q is non-nop */
   480  				q1.Mark |= p.Mark
   481  			}
   482  			continue
   483  
   484  		case ABL,
   485  			obj.ADUFFZERO,
   486  			obj.ADUFFCOPY:
   487  			c.cursym.Func.Text.Mark &^= LEAF
   488  			fallthrough
   489  
   490  		case ACBNZ,
   491  			ACBZ,
   492  			ACBNZW,
   493  			ACBZW,
   494  			ATBZ,
   495  			ATBNZ,
   496  			AB,
   497  			ABEQ,
   498  			ABNE,
   499  			ABCS,
   500  			ABHS,
   501  			ABCC,
   502  			ABLO,
   503  			ABMI,
   504  			ABPL,
   505  			ABVS,
   506  			ABVC,
   507  			ABHI,
   508  			ABLS,
   509  			ABGE,
   510  			ABLT,
   511  			ABGT,
   512  			ABLE,
   513  			AADR, /* strange */
   514  			AADRP:
   515  			q1 = p.Pcond
   516  
   517  			if q1 != nil {
   518  				for q1.As == obj.ANOP {
   519  					q1 = q1.Link
   520  					p.Pcond = q1
   521  				}
   522  			}
   523  
   524  			break
   525  		}
   526  
   527  		q = p
   528  	}
   529  
   530  	var retjmp *obj.LSym
   531  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   532  		o := p.As
   533  		switch o {
   534  		case obj.ATEXT:
   535  			c.cursym.Func.Text = p
   536  			c.autosize = int32(textstksiz)
   537  
   538  			if p.Mark&LEAF != 0 && c.autosize == 0 {
   539  				// A leaf function with no locals has no frame.
   540  				p.From.Sym.Set(obj.AttrNoFrame, true)
   541  			}
   542  
   543  			if !p.From.Sym.NoFrame() {
   544  				// If there is a stack frame at all, it includes
   545  				// space to save the LR.
   546  				c.autosize += 8
   547  			}
   548  
   549  			if c.autosize != 0 {
   550  				extrasize := int32(0)
   551  				if c.autosize%16 == 8 {
   552  					// Allocate extra 8 bytes on the frame top to save FP
   553  					extrasize = 8
   554  				} else if c.autosize&(16-1) == 0 {
   555  					// Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
   556  					extrasize = 16
   557  				} else {
   558  					c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
   559  				}
   560  				c.autosize += extrasize
   561  				c.cursym.Func.Locals += extrasize
   562  
   563  				// low 32 bits for autosize
   564  				// high 32 bits for extrasize
   565  				p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
   566  			} else {
   567  				// NOFRAME
   568  				p.To.Offset = 0
   569  			}
   570  
   571  			if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
   572  				if c.ctxt.Debugvlog {
   573  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
   574  				}
   575  				c.cursym.Func.Text.Mark |= LEAF
   576  			}
   577  
   578  			if cursym.Func.Text.Mark&LEAF != 0 {
   579  				cursym.Set(obj.AttrLeaf, true)
   580  				if p.From.Sym.NoFrame() {
   581  					break
   582  				}
   583  			}
   584  
   585  			if !p.From.Sym.NoSplit() {
   586  				p = c.stacksplit(p, c.autosize) // emit split check
   587  			}
   588  
   589  			aoffset := c.autosize
   590  			if aoffset > 0xF0 {
   591  				aoffset = 0xF0
   592  			}
   593  
   594  			// Frame is non-empty. Make sure to save link register, even if
   595  			// it is a leaf function, so that traceback works.
   596  			q = p
   597  			if c.autosize > aoffset {
   598  				// Frame size is too large for a MOVD.W instruction.
   599  				// Store link register before decrementing SP, so if a signal comes
   600  				// during the execution of the function prologue, the traceback
   601  				// code will not see a half-updated stack frame.
   602  				q = obj.Appendp(q, c.newprog)
   603  				q.Pos = p.Pos
   604  				q.As = ASUB
   605  				q.From.Type = obj.TYPE_CONST
   606  				q.From.Offset = int64(c.autosize)
   607  				q.Reg = REGSP
   608  				q.To.Type = obj.TYPE_REG
   609  				q.To.Reg = REGTMP
   610  
   611  				q = obj.Appendp(q, c.newprog)
   612  				q.Pos = p.Pos
   613  				q.As = AMOVD
   614  				q.From.Type = obj.TYPE_REG
   615  				q.From.Reg = REGLINK
   616  				q.To.Type = obj.TYPE_MEM
   617  				q.To.Reg = REGTMP
   618  
   619  				q1 = obj.Appendp(q, c.newprog)
   620  				q1.Pos = p.Pos
   621  				q1.As = AMOVD
   622  				q1.From.Type = obj.TYPE_REG
   623  				q1.From.Reg = REGTMP
   624  				q1.To.Type = obj.TYPE_REG
   625  				q1.To.Reg = REGSP
   626  				q1.Spadj = c.autosize
   627  			} else {
   628  				// small frame, update SP and save LR in a single MOVD.W instruction
   629  				q1 = obj.Appendp(q, c.newprog)
   630  				q1.As = AMOVD
   631  				q1.Pos = p.Pos
   632  				q1.From.Type = obj.TYPE_REG
   633  				q1.From.Reg = REGLINK
   634  				q1.To.Type = obj.TYPE_MEM
   635  				q1.Scond = C_XPRE
   636  				q1.To.Offset = int64(-aoffset)
   637  				q1.To.Reg = REGSP
   638  				q1.Spadj = aoffset
   639  			}
   640  
   641  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   642  				q1 = obj.Appendp(q1, c.newprog)
   643  				q1.Pos = p.Pos
   644  				q1.As = AMOVD
   645  				q1.From.Type = obj.TYPE_REG
   646  				q1.From.Reg = REGFP
   647  				q1.To.Type = obj.TYPE_MEM
   648  				q1.To.Reg = REGSP
   649  				q1.To.Offset = -8
   650  
   651  				q1 = obj.Appendp(q1, c.newprog)
   652  				q1.Pos = p.Pos
   653  				q1.As = ASUB
   654  				q1.From.Type = obj.TYPE_CONST
   655  				q1.From.Offset = 8
   656  				q1.Reg = REGSP
   657  				q1.To.Type = obj.TYPE_REG
   658  				q1.To.Reg = REGFP
   659  			}
   660  
   661  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   662  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   663  				//
   664  				//  MOV  g_panic(g), R1
   665  				//  CBNZ checkargp
   666  				// end:
   667  				//  NOP
   668  				// ... function body ...
   669  				// checkargp:
   670  				//  MOV  panic_argp(R1), R2
   671  				//  ADD  $(autosize+8), RSP, R3
   672  				//  CMP  R2, R3
   673  				//  BNE  end
   674  				//  ADD  $8, RSP, R4
   675  				//  MOVD R4, panic_argp(R1)
   676  				//  B    end
   677  				//
   678  				// The NOP is needed to give the jumps somewhere to land.
   679  				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
   680  				q = q1
   681  
   682  				// MOV g_panic(g), R1
   683  				q = obj.Appendp(q, c.newprog)
   684  				q.As = AMOVD
   685  				q.From.Type = obj.TYPE_MEM
   686  				q.From.Reg = REGG
   687  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   688  				q.To.Type = obj.TYPE_REG
   689  				q.To.Reg = REG_R1
   690  
   691  				// CBNZ R1, checkargp
   692  				cbnz := obj.Appendp(q, c.newprog)
   693  				cbnz.As = ACBNZ
   694  				cbnz.From.Type = obj.TYPE_REG
   695  				cbnz.From.Reg = REG_R1
   696  				cbnz.To.Type = obj.TYPE_BRANCH
   697  
   698  				// Empty branch target at the top of the function body
   699  				end := obj.Appendp(cbnz, c.newprog)
   700  				end.As = obj.ANOP
   701  
   702  				// find the end of the function
   703  				var last *obj.Prog
   704  				for last = end; last.Link != nil; last = last.Link {
   705  				}
   706  
   707  				// MOV panic_argp(R1), R2
   708  				mov := obj.Appendp(last, c.newprog)
   709  				mov.As = AMOVD
   710  				mov.From.Type = obj.TYPE_MEM
   711  				mov.From.Reg = REG_R1
   712  				mov.From.Offset = 0 // Panic.argp
   713  				mov.To.Type = obj.TYPE_REG
   714  				mov.To.Reg = REG_R2
   715  
   716  				// CBNZ branches to the MOV above
   717  				cbnz.Pcond = mov
   718  
   719  				// ADD $(autosize+8), SP, R3
   720  				q = obj.Appendp(mov, c.newprog)
   721  				q.As = AADD
   722  				q.From.Type = obj.TYPE_CONST
   723  				q.From.Offset = int64(c.autosize) + 8
   724  				q.Reg = REGSP
   725  				q.To.Type = obj.TYPE_REG
   726  				q.To.Reg = REG_R3
   727  
   728  				// CMP R2, R3
   729  				q = obj.Appendp(q, c.newprog)
   730  				q.As = ACMP
   731  				q.From.Type = obj.TYPE_REG
   732  				q.From.Reg = REG_R2
   733  				q.Reg = REG_R3
   734  
   735  				// BNE end
   736  				q = obj.Appendp(q, c.newprog)
   737  				q.As = ABNE
   738  				q.To.Type = obj.TYPE_BRANCH
   739  				q.Pcond = end
   740  
   741  				// ADD $8, SP, R4
   742  				q = obj.Appendp(q, c.newprog)
   743  				q.As = AADD
   744  				q.From.Type = obj.TYPE_CONST
   745  				q.From.Offset = 8
   746  				q.Reg = REGSP
   747  				q.To.Type = obj.TYPE_REG
   748  				q.To.Reg = REG_R4
   749  
   750  				// MOV R4, panic_argp(R1)
   751  				q = obj.Appendp(q, c.newprog)
   752  				q.As = AMOVD
   753  				q.From.Type = obj.TYPE_REG
   754  				q.From.Reg = REG_R4
   755  				q.To.Type = obj.TYPE_MEM
   756  				q.To.Reg = REG_R1
   757  				q.To.Offset = 0 // Panic.argp
   758  
   759  				// B end
   760  				q = obj.Appendp(q, c.newprog)
   761  				q.As = AB
   762  				q.To.Type = obj.TYPE_BRANCH
   763  				q.Pcond = end
   764  			}
   765  
   766  		case obj.ARET:
   767  			nocache(p)
   768  			if p.From.Type == obj.TYPE_CONST {
   769  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   770  				break
   771  			}
   772  
   773  			retjmp = p.To.Sym
   774  			p.To = obj.Addr{}
   775  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   776  				if c.autosize != 0 {
   777  					p.As = AADD
   778  					p.From.Type = obj.TYPE_CONST
   779  					p.From.Offset = int64(c.autosize)
   780  					p.To.Type = obj.TYPE_REG
   781  					p.To.Reg = REGSP
   782  					p.Spadj = -c.autosize
   783  
   784  					if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   785  						p = obj.Appendp(p, c.newprog)
   786  						p.As = ASUB
   787  						p.From.Type = obj.TYPE_CONST
   788  						p.From.Offset = 8
   789  						p.Reg = REGSP
   790  						p.To.Type = obj.TYPE_REG
   791  						p.To.Reg = REGFP
   792  					}
   793  				}
   794  			} else {
   795  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   796  
   797  				if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   798  					p.As = AMOVD
   799  					p.From.Type = obj.TYPE_MEM
   800  					p.From.Reg = REGSP
   801  					p.From.Offset = -8
   802  					p.To.Type = obj.TYPE_REG
   803  					p.To.Reg = REGFP
   804  					p = obj.Appendp(p, c.newprog)
   805  				}
   806  
   807  				aoffset := c.autosize
   808  
   809  				if aoffset > 0xF0 {
   810  					aoffset = 0xF0
   811  				}
   812  				p.As = AMOVD
   813  				p.From.Type = obj.TYPE_MEM
   814  				p.Scond = C_XPOST
   815  				p.From.Offset = int64(aoffset)
   816  				p.From.Reg = REGSP
   817  				p.To.Type = obj.TYPE_REG
   818  				p.To.Reg = REGLINK
   819  				p.Spadj = -aoffset
   820  				if c.autosize > aoffset {
   821  					q = newprog()
   822  					q.As = AADD
   823  					q.From.Type = obj.TYPE_CONST
   824  					q.From.Offset = int64(c.autosize) - int64(aoffset)
   825  					q.To.Type = obj.TYPE_REG
   826  					q.To.Reg = REGSP
   827  					q.Link = p.Link
   828  					q.Spadj = int32(-q.From.Offset)
   829  					q.Pos = p.Pos
   830  					p.Link = q
   831  					p = q
   832  				}
   833  			}
   834  
   835  			if p.As != obj.ARET {
   836  				q = newprog()
   837  				q.Pos = p.Pos
   838  				q.Link = p.Link
   839  				p.Link = q
   840  				p = q
   841  			}
   842  
   843  			if retjmp != nil { // retjmp
   844  				p.As = AB
   845  				p.To.Type = obj.TYPE_BRANCH
   846  				p.To.Sym = retjmp
   847  				p.Spadj = +c.autosize
   848  				break
   849  			}
   850  
   851  			p.As = obj.ARET
   852  			p.To.Type = obj.TYPE_MEM
   853  			p.To.Offset = 0
   854  			p.To.Reg = REGLINK
   855  			p.Spadj = +c.autosize
   856  
   857  		case AADD, ASUB:
   858  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   859  				if p.As == AADD {
   860  					p.Spadj = int32(-p.From.Offset)
   861  				} else {
   862  					p.Spadj = int32(+p.From.Offset)
   863  				}
   864  			}
   865  
   866  		case obj.AGETCALLERPC:
   867  			if cursym.Leaf() {
   868  				/* MOVD LR, Rd */
   869  				p.As = AMOVD
   870  				p.From.Type = obj.TYPE_REG
   871  				p.From.Reg = REGLINK
   872  			} else {
   873  				/* MOVD (RSP), Rd */
   874  				p.As = AMOVD
   875  				p.From.Type = obj.TYPE_MEM
   876  				p.From.Reg = REGSP
   877  			}
   878  
   879  		case obj.ADUFFCOPY:
   880  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   881  				//  ADR ret_addr, R27
   882  				//  STP (FP, R27), -24(SP)
   883  				//  SUB 24, SP, FP
   884  				//  DUFFCOPY
   885  				// ret_addr:
   886  				//  SUB 8, SP, FP
   887  
   888  				q1 := p
   889  				// copy DUFFCOPY from q1 to q4
   890  				q4 := obj.Appendp(p, c.newprog)
   891  				q4.Pos = p.Pos
   892  				q4.As = obj.ADUFFCOPY
   893  				q4.To = p.To
   894  
   895  				q1.As = AADR
   896  				q1.From.Type = obj.TYPE_BRANCH
   897  				q1.To.Type = obj.TYPE_REG
   898  				q1.To.Reg = REG_R27
   899  
   900  				q2 := obj.Appendp(q1, c.newprog)
   901  				q2.Pos = p.Pos
   902  				q2.As = ASTP
   903  				q2.From.Type = obj.TYPE_REGREG
   904  				q2.From.Reg = REGFP
   905  				q2.From.Offset = int64(REG_R27)
   906  				q2.To.Type = obj.TYPE_MEM
   907  				q2.To.Reg = REGSP
   908  				q2.To.Offset = -24
   909  
   910  				// maintaine FP for DUFFCOPY
   911  				q3 := obj.Appendp(q2, c.newprog)
   912  				q3.Pos = p.Pos
   913  				q3.As = ASUB
   914  				q3.From.Type = obj.TYPE_CONST
   915  				q3.From.Offset = 24
   916  				q3.Reg = REGSP
   917  				q3.To.Type = obj.TYPE_REG
   918  				q3.To.Reg = REGFP
   919  
   920  				q5 := obj.Appendp(q4, c.newprog)
   921  				q5.Pos = p.Pos
   922  				q5.As = ASUB
   923  				q5.From.Type = obj.TYPE_CONST
   924  				q5.From.Offset = 8
   925  				q5.Reg = REGSP
   926  				q5.To.Type = obj.TYPE_REG
   927  				q5.To.Reg = REGFP
   928  				q1.Pcond = q5
   929  				p = q5
   930  			}
   931  
   932  		case obj.ADUFFZERO:
   933  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   934  				//  ADR ret_addr, R27
   935  				//  STP (FP, R27), -24(SP)
   936  				//  SUB 24, SP, FP
   937  				//  DUFFZERO
   938  				// ret_addr:
   939  				//  SUB 8, SP, FP
   940  
   941  				q1 := p
   942  				// copy DUFFZERO from q1 to q4
   943  				q4 := obj.Appendp(p, c.newprog)
   944  				q4.Pos = p.Pos
   945  				q4.As = obj.ADUFFZERO
   946  				q4.To = p.To
   947  
   948  				q1.As = AADR
   949  				q1.From.Type = obj.TYPE_BRANCH
   950  				q1.To.Type = obj.TYPE_REG
   951  				q1.To.Reg = REG_R27
   952  
   953  				q2 := obj.Appendp(q1, c.newprog)
   954  				q2.Pos = p.Pos
   955  				q2.As = ASTP
   956  				q2.From.Type = obj.TYPE_REGREG
   957  				q2.From.Reg = REGFP
   958  				q2.From.Offset = int64(REG_R27)
   959  				q2.To.Type = obj.TYPE_MEM
   960  				q2.To.Reg = REGSP
   961  				q2.To.Offset = -24
   962  
   963  				// maintaine FP for DUFFZERO
   964  				q3 := obj.Appendp(q2, c.newprog)
   965  				q3.Pos = p.Pos
   966  				q3.As = ASUB
   967  				q3.From.Type = obj.TYPE_CONST
   968  				q3.From.Offset = 24
   969  				q3.Reg = REGSP
   970  				q3.To.Type = obj.TYPE_REG
   971  				q3.To.Reg = REGFP
   972  
   973  				q5 := obj.Appendp(q4, c.newprog)
   974  				q5.Pos = p.Pos
   975  				q5.As = ASUB
   976  				q5.From.Type = obj.TYPE_CONST
   977  				q5.From.Offset = 8
   978  				q5.Reg = REGSP
   979  				q5.To.Type = obj.TYPE_REG
   980  				q5.To.Reg = REGFP
   981  				q1.Pcond = q5
   982  				p = q5
   983  			}
   984  		}
   985  	}
   986  }
   987  
   988  func nocache(p *obj.Prog) {
   989  	p.Optab = 0
   990  	p.From.Class = 0
   991  	p.To.Class = 0
   992  }
   993  
   994  var unaryDst = map[obj.As]bool{
   995  	AWORD:  true,
   996  	ADWORD: true,
   997  	ABL:    true,
   998  	AB:     true,
   999  	ACLREX: true,
  1000  }
  1001  
  1002  var Linkarm64 = obj.LinkArch{
  1003  	Arch:           sys.ArchARM64,
  1004  	Init:           buildop,
  1005  	Preprocess:     preprocess,
  1006  	Assemble:       span7,
  1007  	Progedit:       progedit,
  1008  	UnaryDst:       unaryDst,
  1009  	DWARFRegisters: ARM64DWARFRegisters,
  1010  }