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