github.com/hikaru7719/go@v0.0.0-20181025140707-c8b2ac68906a/src/cmd/internal/obj/arm64/obj7.go (about)

     1  // cmd/7l/noop.c, cmd/7l/obj.c, cmd/ld/pass.c from Vita Nuova.
     2  // https://code.google.com/p/ken-cc/source/browse/
     3  //
     4  // 	Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
     5  // 	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
     6  // 	Portions Copyright © 1997-1999 Vita Nuova Limited
     7  // 	Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.com)
     8  // 	Portions Copyright © 2004,2006 Bruce Ellis
     9  // 	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
    10  // 	Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others
    11  // 	Portions Copyright © 2009 The Go Authors. All rights reserved.
    12  //
    13  // Permission is hereby granted, free of charge, to any person obtaining a copy
    14  // of this software and associated documentation files (the "Software"), to deal
    15  // in the Software without restriction, including without limitation the rights
    16  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    17  // copies of the Software, and to permit persons to whom the Software is
    18  // furnished to do so, subject to the following conditions:
    19  //
    20  // The above copyright notice and this permission notice shall be included in
    21  // all copies or substantial portions of the Software.
    22  //
    23  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    24  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    25  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
    26  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    27  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    28  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    29  // THE SOFTWARE.
    30  
    31  package arm64
    32  
    33  import (
    34  	"cmd/internal/obj"
    35  	"cmd/internal/objabi"
    36  	"cmd/internal/sys"
    37  	"math"
    38  )
    39  
    40  var complements = []obj.As{
    41  	AADD:  ASUB,
    42  	AADDW: ASUBW,
    43  	ASUB:  AADD,
    44  	ASUBW: AADDW,
    45  	ACMP:  ACMN,
    46  	ACMPW: ACMNW,
    47  	ACMN:  ACMP,
    48  	ACMNW: ACMPW,
    49  }
    50  
    51  func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
    52  	// MOV	g_stackguard(g), R1
    53  	p = obj.Appendp(p, c.newprog)
    54  
    55  	p.As = AMOVD
    56  	p.From.Type = obj.TYPE_MEM
    57  	p.From.Reg = REGG
    58  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
    59  	if c.cursym.CFunc() {
    60  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
    61  	}
    62  	p.To.Type = obj.TYPE_REG
    63  	p.To.Reg = REG_R1
    64  
    65  	q := (*obj.Prog)(nil)
    66  	if framesize <= objabi.StackSmall {
    67  		// small stack: SP < stackguard
    68  		//	MOV	SP, R2
    69  		//	CMP	stackguard, R2
    70  		p = obj.Appendp(p, c.newprog)
    71  
    72  		p.As = AMOVD
    73  		p.From.Type = obj.TYPE_REG
    74  		p.From.Reg = REGSP
    75  		p.To.Type = obj.TYPE_REG
    76  		p.To.Reg = REG_R2
    77  
    78  		p = obj.Appendp(p, c.newprog)
    79  		p.As = ACMP
    80  		p.From.Type = obj.TYPE_REG
    81  		p.From.Reg = REG_R1
    82  		p.Reg = REG_R2
    83  	} else if framesize <= objabi.StackBig {
    84  		// large stack: SP-framesize < stackguard-StackSmall
    85  		//	SUB	$(framesize-StackSmall), SP, R2
    86  		//	CMP	stackguard, R2
    87  		p = obj.Appendp(p, c.newprog)
    88  
    89  		p.As = ASUB
    90  		p.From.Type = obj.TYPE_CONST
    91  		p.From.Offset = int64(framesize) - objabi.StackSmall
    92  		p.Reg = REGSP
    93  		p.To.Type = obj.TYPE_REG
    94  		p.To.Reg = REG_R2
    95  
    96  		p = obj.Appendp(p, c.newprog)
    97  		p.As = ACMP
    98  		p.From.Type = obj.TYPE_REG
    99  		p.From.Reg = REG_R1
   100  		p.Reg = REG_R2
   101  	} else {
   102  		// Such a large stack we need to protect against wraparound
   103  		// if SP is close to zero.
   104  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
   105  		// The +StackGuard on both sides is required to keep the left side positive:
   106  		// SP is allowed to be slightly below stackguard. See stack.h.
   107  		//	CMP	$StackPreempt, R1
   108  		//	BEQ	label_of_call_to_morestack
   109  		//	ADD	$StackGuard, SP, R2
   110  		//	SUB	R1, R2
   111  		//	MOV	$(framesize+(StackGuard-StackSmall)), R3
   112  		//	CMP	R3, R2
   113  		p = obj.Appendp(p, c.newprog)
   114  
   115  		p.As = ACMP
   116  		p.From.Type = obj.TYPE_CONST
   117  		p.From.Offset = objabi.StackPreempt
   118  		p.Reg = REG_R1
   119  
   120  		p = obj.Appendp(p, c.newprog)
   121  		q = p
   122  		p.As = ABEQ
   123  		p.To.Type = obj.TYPE_BRANCH
   124  
   125  		p = obj.Appendp(p, c.newprog)
   126  		p.As = AADD
   127  		p.From.Type = obj.TYPE_CONST
   128  		p.From.Offset = objabi.StackGuard
   129  		p.Reg = REGSP
   130  		p.To.Type = obj.TYPE_REG
   131  		p.To.Reg = REG_R2
   132  
   133  		p = obj.Appendp(p, c.newprog)
   134  		p.As = ASUB
   135  		p.From.Type = obj.TYPE_REG
   136  		p.From.Reg = REG_R1
   137  		p.To.Type = obj.TYPE_REG
   138  		p.To.Reg = REG_R2
   139  
   140  		p = obj.Appendp(p, c.newprog)
   141  		p.As = AMOVD
   142  		p.From.Type = obj.TYPE_CONST
   143  		p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
   144  		p.To.Type = obj.TYPE_REG
   145  		p.To.Reg = REG_R3
   146  
   147  		p = obj.Appendp(p, c.newprog)
   148  		p.As = ACMP
   149  		p.From.Type = obj.TYPE_REG
   150  		p.From.Reg = REG_R3
   151  		p.Reg = REG_R2
   152  	}
   153  
   154  	// BLS	do-morestack
   155  	bls := obj.Appendp(p, c.newprog)
   156  	bls.As = ABLS
   157  	bls.To.Type = obj.TYPE_BRANCH
   158  
   159  	var last *obj.Prog
   160  	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
   161  	}
   162  
   163  	// Now we are at the end of the function, but logically
   164  	// we are still in function prologue. We need to fix the
   165  	// SP data and PCDATA.
   166  	spfix := obj.Appendp(last, c.newprog)
   167  	spfix.As = obj.ANOP
   168  	spfix.Spadj = -framesize
   169  
   170  	pcdata := 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  	switch p.As {
   315  	case AANDW, AORRW, AEORW, AANDSW, ATSTW:
   316  		if p.From.Type == obj.TYPE_CONST {
   317  			v := p.From.Offset & 0xffffffff
   318  			p.From.Offset = v | v<<32
   319  		}
   320  	}
   321  
   322  	if c.ctxt.Flag_dynlink {
   323  		c.rewriteToUseGot(p)
   324  	}
   325  }
   326  
   327  // Rewrite p, if necessary, to access global data via the global offset table.
   328  func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
   329  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   330  		//     ADUFFxxx $offset
   331  		// becomes
   332  		//     MOVD runtime.duffxxx@GOT, REGTMP
   333  		//     ADD $offset, REGTMP
   334  		//     CALL REGTMP
   335  		var sym *obj.LSym
   336  		if p.As == obj.ADUFFZERO {
   337  			sym = c.ctxt.Lookup("runtime.duffzero")
   338  		} else {
   339  			sym = c.ctxt.Lookup("runtime.duffcopy")
   340  		}
   341  		offset := p.To.Offset
   342  		p.As = AMOVD
   343  		p.From.Type = obj.TYPE_MEM
   344  		p.From.Name = obj.NAME_GOTREF
   345  		p.From.Sym = sym
   346  		p.To.Type = obj.TYPE_REG
   347  		p.To.Reg = REGTMP
   348  		p.To.Name = obj.NAME_NONE
   349  		p.To.Offset = 0
   350  		p.To.Sym = nil
   351  		p1 := obj.Appendp(p, c.newprog)
   352  		p1.As = AADD
   353  		p1.From.Type = obj.TYPE_CONST
   354  		p1.From.Offset = offset
   355  		p1.To.Type = obj.TYPE_REG
   356  		p1.To.Reg = REGTMP
   357  		p2 := obj.Appendp(p1, c.newprog)
   358  		p2.As = obj.ACALL
   359  		p2.To.Type = obj.TYPE_REG
   360  		p2.To.Reg = REGTMP
   361  	}
   362  
   363  	// We only care about global data: NAME_EXTERN means a global
   364  	// symbol in the Go sense, and p.Sym.Local is true for a few
   365  	// internally defined symbols.
   366  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   367  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   368  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   369  		if p.As != AMOVD {
   370  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   371  		}
   372  		if p.To.Type != obj.TYPE_REG {
   373  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   374  		}
   375  		p.From.Type = obj.TYPE_MEM
   376  		p.From.Name = obj.NAME_GOTREF
   377  		if p.From.Offset != 0 {
   378  			q := obj.Appendp(p, c.newprog)
   379  			q.As = AADD
   380  			q.From.Type = obj.TYPE_CONST
   381  			q.From.Offset = p.From.Offset
   382  			q.To = p.To
   383  			p.From.Offset = 0
   384  		}
   385  	}
   386  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
   387  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   388  	}
   389  	var source *obj.Addr
   390  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   391  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   392  	// An addition may be inserted between the two MOVs if there is an offset.
   393  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
   394  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   395  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   396  		}
   397  		source = &p.From
   398  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
   399  		source = &p.To
   400  	} else {
   401  		return
   402  	}
   403  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   404  		return
   405  	}
   406  	if source.Sym.Type == objabi.STLSBSS {
   407  		return
   408  	}
   409  	if source.Type != obj.TYPE_MEM {
   410  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
   411  	}
   412  	p1 := obj.Appendp(p, c.newprog)
   413  	p2 := obj.Appendp(p1, c.newprog)
   414  	p1.As = AMOVD
   415  	p1.From.Type = obj.TYPE_MEM
   416  	p1.From.Sym = source.Sym
   417  	p1.From.Name = obj.NAME_GOTREF
   418  	p1.To.Type = obj.TYPE_REG
   419  	p1.To.Reg = REGTMP
   420  
   421  	p2.As = p.As
   422  	p2.From = p.From
   423  	p2.To = p.To
   424  	if p.From.Name == obj.NAME_EXTERN {
   425  		p2.From.Reg = REGTMP
   426  		p2.From.Name = obj.NAME_NONE
   427  		p2.From.Sym = nil
   428  	} else if p.To.Name == obj.NAME_EXTERN {
   429  		p2.To.Reg = REGTMP
   430  		p2.To.Name = obj.NAME_NONE
   431  		p2.To.Sym = nil
   432  	} else {
   433  		return
   434  	}
   435  	obj.Nopout(p)
   436  }
   437  
   438  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
   439  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
   440  		return
   441  	}
   442  
   443  	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
   444  
   445  	p := c.cursym.Func.Text
   446  	textstksiz := p.To.Offset
   447  	if textstksiz == -8 {
   448  		// Historical way to mark NOFRAME.
   449  		p.From.Sym.Set(obj.AttrNoFrame, true)
   450  		textstksiz = 0
   451  	}
   452  	if textstksiz < 0 {
   453  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
   454  	}
   455  	if p.From.Sym.NoFrame() {
   456  		if textstksiz != 0 {
   457  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
   458  		}
   459  	}
   460  
   461  	c.cursym.Func.Args = p.To.Val.(int32)
   462  	c.cursym.Func.Locals = int32(textstksiz)
   463  
   464  	/*
   465  	 * find leaf subroutines
   466  	 * strip NOPs
   467  	 * expand RET
   468  	 */
   469  	q := (*obj.Prog)(nil)
   470  	var q1 *obj.Prog
   471  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   472  		switch p.As {
   473  		case obj.ATEXT:
   474  			p.Mark |= LEAF
   475  
   476  		case obj.ARET:
   477  			break
   478  
   479  		case obj.ANOP:
   480  			if p.Link != nil {
   481  				q1 = p.Link
   482  				q.Link = q1 /* q is non-nop */
   483  				q1.Mark |= p.Mark
   484  			}
   485  			continue
   486  
   487  		case ABL,
   488  			obj.ADUFFZERO,
   489  			obj.ADUFFCOPY:
   490  			c.cursym.Func.Text.Mark &^= LEAF
   491  			fallthrough
   492  
   493  		case ACBNZ,
   494  			ACBZ,
   495  			ACBNZW,
   496  			ACBZW,
   497  			ATBZ,
   498  			ATBNZ,
   499  			AB,
   500  			ABEQ,
   501  			ABNE,
   502  			ABCS,
   503  			ABHS,
   504  			ABCC,
   505  			ABLO,
   506  			ABMI,
   507  			ABPL,
   508  			ABVS,
   509  			ABVC,
   510  			ABHI,
   511  			ABLS,
   512  			ABGE,
   513  			ABLT,
   514  			ABGT,
   515  			ABLE,
   516  			AADR, /* strange */
   517  			AADRP:
   518  			q1 = p.Pcond
   519  
   520  			if q1 != nil {
   521  				for q1.As == obj.ANOP {
   522  					q1 = q1.Link
   523  					p.Pcond = q1
   524  				}
   525  			}
   526  
   527  			break
   528  		}
   529  
   530  		q = p
   531  	}
   532  
   533  	var retjmp *obj.LSym
   534  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
   535  		o := p.As
   536  		switch o {
   537  		case obj.ATEXT:
   538  			c.cursym.Func.Text = p
   539  			c.autosize = int32(textstksiz)
   540  
   541  			if p.Mark&LEAF != 0 && c.autosize == 0 {
   542  				// A leaf function with no locals has no frame.
   543  				p.From.Sym.Set(obj.AttrNoFrame, true)
   544  			}
   545  
   546  			if !p.From.Sym.NoFrame() {
   547  				// If there is a stack frame at all, it includes
   548  				// space to save the LR.
   549  				c.autosize += 8
   550  			}
   551  
   552  			if c.autosize != 0 {
   553  				extrasize := int32(0)
   554  				if c.autosize%16 == 8 {
   555  					// Allocate extra 8 bytes on the frame top to save FP
   556  					extrasize = 8
   557  				} else if c.autosize&(16-1) == 0 {
   558  					// Allocate extra 16 bytes to save FP for the old frame whose size is 8 mod 16
   559  					extrasize = 16
   560  				} else {
   561  					c.ctxt.Diag("%v: unaligned frame size %d - must be 16 aligned", p, c.autosize-8)
   562  				}
   563  				c.autosize += extrasize
   564  				c.cursym.Func.Locals += extrasize
   565  
   566  				// low 32 bits for autosize
   567  				// high 32 bits for extrasize
   568  				p.To.Offset = int64(c.autosize) | int64(extrasize)<<32
   569  			} else {
   570  				// NOFRAME
   571  				p.To.Offset = 0
   572  			}
   573  
   574  			if c.autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
   575  				if c.ctxt.Debugvlog {
   576  					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
   577  				}
   578  				c.cursym.Func.Text.Mark |= LEAF
   579  			}
   580  
   581  			if cursym.Func.Text.Mark&LEAF != 0 {
   582  				cursym.Set(obj.AttrLeaf, true)
   583  				if p.From.Sym.NoFrame() {
   584  					break
   585  				}
   586  			}
   587  
   588  			if !p.From.Sym.NoSplit() {
   589  				p = c.stacksplit(p, c.autosize) // emit split check
   590  			}
   591  
   592  			aoffset := c.autosize
   593  			if aoffset > 0xF0 {
   594  				aoffset = 0xF0
   595  			}
   596  
   597  			// Frame is non-empty. Make sure to save link register, even if
   598  			// it is a leaf function, so that traceback works.
   599  			q = p
   600  			if c.autosize > aoffset {
   601  				// Frame size is too large for a MOVD.W instruction.
   602  				// Store link register before decrementing SP, so if a signal comes
   603  				// during the execution of the function prologue, the traceback
   604  				// code will not see a half-updated stack frame.
   605  				q = obj.Appendp(q, c.newprog)
   606  				q.Pos = p.Pos
   607  				q.As = ASUB
   608  				q.From.Type = obj.TYPE_CONST
   609  				q.From.Offset = int64(c.autosize)
   610  				q.Reg = REGSP
   611  				q.To.Type = obj.TYPE_REG
   612  				q.To.Reg = REGTMP
   613  
   614  				q = obj.Appendp(q, c.newprog)
   615  				q.Pos = p.Pos
   616  				q.As = AMOVD
   617  				q.From.Type = obj.TYPE_REG
   618  				q.From.Reg = REGLINK
   619  				q.To.Type = obj.TYPE_MEM
   620  				q.To.Reg = REGTMP
   621  
   622  				q1 = obj.Appendp(q, c.newprog)
   623  				q1.Pos = p.Pos
   624  				q1.As = AMOVD
   625  				q1.From.Type = obj.TYPE_REG
   626  				q1.From.Reg = REGTMP
   627  				q1.To.Type = obj.TYPE_REG
   628  				q1.To.Reg = REGSP
   629  				q1.Spadj = c.autosize
   630  			} else {
   631  				// small frame, update SP and save LR in a single MOVD.W instruction
   632  				q1 = obj.Appendp(q, c.newprog)
   633  				q1.As = AMOVD
   634  				q1.Pos = p.Pos
   635  				q1.From.Type = obj.TYPE_REG
   636  				q1.From.Reg = REGLINK
   637  				q1.To.Type = obj.TYPE_MEM
   638  				q1.Scond = C_XPRE
   639  				q1.To.Offset = int64(-aoffset)
   640  				q1.To.Reg = REGSP
   641  				q1.Spadj = aoffset
   642  			}
   643  
   644  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   645  				q1 = obj.Appendp(q1, c.newprog)
   646  				q1.Pos = p.Pos
   647  				q1.As = AMOVD
   648  				q1.From.Type = obj.TYPE_REG
   649  				q1.From.Reg = REGFP
   650  				q1.To.Type = obj.TYPE_MEM
   651  				q1.To.Reg = REGSP
   652  				q1.To.Offset = -8
   653  
   654  				q1 = obj.Appendp(q1, c.newprog)
   655  				q1.Pos = p.Pos
   656  				q1.As = ASUB
   657  				q1.From.Type = obj.TYPE_CONST
   658  				q1.From.Offset = 8
   659  				q1.Reg = REGSP
   660  				q1.To.Type = obj.TYPE_REG
   661  				q1.To.Reg = REGFP
   662  			}
   663  
   664  			if c.cursym.Func.Text.From.Sym.Wrapper() {
   665  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   666  				//
   667  				//	MOV  g_panic(g), R1
   668  				//	CBNZ checkargp
   669  				// end:
   670  				//	NOP
   671  				// ... function body ...
   672  				// checkargp:
   673  				//	MOV  panic_argp(R1), R2
   674  				//	ADD  $(autosize+8), RSP, R3
   675  				//	CMP  R2, R3
   676  				//	BNE  end
   677  				//	ADD  $8, RSP, R4
   678  				//	MOVD R4, panic_argp(R1)
   679  				//	B    end
   680  				//
   681  				// The NOP is needed to give the jumps somewhere to land.
   682  				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
   683  				q = q1
   684  
   685  				// MOV g_panic(g), R1
   686  				q = obj.Appendp(q, c.newprog)
   687  				q.As = AMOVD
   688  				q.From.Type = obj.TYPE_MEM
   689  				q.From.Reg = REGG
   690  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
   691  				q.To.Type = obj.TYPE_REG
   692  				q.To.Reg = REG_R1
   693  
   694  				// CBNZ R1, checkargp
   695  				cbnz := obj.Appendp(q, c.newprog)
   696  				cbnz.As = ACBNZ
   697  				cbnz.From.Type = obj.TYPE_REG
   698  				cbnz.From.Reg = REG_R1
   699  				cbnz.To.Type = obj.TYPE_BRANCH
   700  
   701  				// Empty branch target at the top of the function body
   702  				end := obj.Appendp(cbnz, c.newprog)
   703  				end.As = obj.ANOP
   704  
   705  				// find the end of the function
   706  				var last *obj.Prog
   707  				for last = end; last.Link != nil; last = last.Link {
   708  				}
   709  
   710  				// MOV panic_argp(R1), R2
   711  				mov := obj.Appendp(last, c.newprog)
   712  				mov.As = AMOVD
   713  				mov.From.Type = obj.TYPE_MEM
   714  				mov.From.Reg = REG_R1
   715  				mov.From.Offset = 0 // Panic.argp
   716  				mov.To.Type = obj.TYPE_REG
   717  				mov.To.Reg = REG_R2
   718  
   719  				// CBNZ branches to the MOV above
   720  				cbnz.Pcond = mov
   721  
   722  				// ADD $(autosize+8), SP, R3
   723  				q = obj.Appendp(mov, c.newprog)
   724  				q.As = AADD
   725  				q.From.Type = obj.TYPE_CONST
   726  				q.From.Offset = int64(c.autosize) + 8
   727  				q.Reg = REGSP
   728  				q.To.Type = obj.TYPE_REG
   729  				q.To.Reg = REG_R3
   730  
   731  				// CMP R2, R3
   732  				q = obj.Appendp(q, c.newprog)
   733  				q.As = ACMP
   734  				q.From.Type = obj.TYPE_REG
   735  				q.From.Reg = REG_R2
   736  				q.Reg = REG_R3
   737  
   738  				// BNE end
   739  				q = obj.Appendp(q, c.newprog)
   740  				q.As = ABNE
   741  				q.To.Type = obj.TYPE_BRANCH
   742  				q.Pcond = end
   743  
   744  				// ADD $8, SP, R4
   745  				q = obj.Appendp(q, c.newprog)
   746  				q.As = AADD
   747  				q.From.Type = obj.TYPE_CONST
   748  				q.From.Offset = 8
   749  				q.Reg = REGSP
   750  				q.To.Type = obj.TYPE_REG
   751  				q.To.Reg = REG_R4
   752  
   753  				// MOV R4, panic_argp(R1)
   754  				q = obj.Appendp(q, c.newprog)
   755  				q.As = AMOVD
   756  				q.From.Type = obj.TYPE_REG
   757  				q.From.Reg = REG_R4
   758  				q.To.Type = obj.TYPE_MEM
   759  				q.To.Reg = REG_R1
   760  				q.To.Offset = 0 // Panic.argp
   761  
   762  				// B end
   763  				q = obj.Appendp(q, c.newprog)
   764  				q.As = AB
   765  				q.To.Type = obj.TYPE_BRANCH
   766  				q.Pcond = end
   767  			}
   768  
   769  		case obj.ARET:
   770  			nocache(p)
   771  			if p.From.Type == obj.TYPE_CONST {
   772  				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
   773  				break
   774  			}
   775  
   776  			retjmp = p.To.Sym
   777  			p.To = obj.Addr{}
   778  			if c.cursym.Func.Text.Mark&LEAF != 0 {
   779  				if c.autosize != 0 {
   780  					p.As = AADD
   781  					p.From.Type = obj.TYPE_CONST
   782  					p.From.Offset = int64(c.autosize)
   783  					p.To.Type = obj.TYPE_REG
   784  					p.To.Reg = REGSP
   785  					p.Spadj = -c.autosize
   786  
   787  					if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   788  						p = obj.Appendp(p, c.newprog)
   789  						p.As = ASUB
   790  						p.From.Type = obj.TYPE_CONST
   791  						p.From.Offset = 8
   792  						p.Reg = REGSP
   793  						p.To.Type = obj.TYPE_REG
   794  						p.To.Reg = REGFP
   795  					}
   796  				}
   797  			} else {
   798  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   799  
   800  				if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   801  					p.As = AMOVD
   802  					p.From.Type = obj.TYPE_MEM
   803  					p.From.Reg = REGSP
   804  					p.From.Offset = -8
   805  					p.To.Type = obj.TYPE_REG
   806  					p.To.Reg = REGFP
   807  					p = obj.Appendp(p, c.newprog)
   808  				}
   809  
   810  				aoffset := c.autosize
   811  
   812  				if aoffset > 0xF0 {
   813  					aoffset = 0xF0
   814  				}
   815  				p.As = AMOVD
   816  				p.From.Type = obj.TYPE_MEM
   817  				p.Scond = C_XPOST
   818  				p.From.Offset = int64(aoffset)
   819  				p.From.Reg = REGSP
   820  				p.To.Type = obj.TYPE_REG
   821  				p.To.Reg = REGLINK
   822  				p.Spadj = -aoffset
   823  				if c.autosize > aoffset {
   824  					q = newprog()
   825  					q.As = AADD
   826  					q.From.Type = obj.TYPE_CONST
   827  					q.From.Offset = int64(c.autosize) - int64(aoffset)
   828  					q.To.Type = obj.TYPE_REG
   829  					q.To.Reg = REGSP
   830  					q.Link = p.Link
   831  					q.Spadj = int32(-q.From.Offset)
   832  					q.Pos = p.Pos
   833  					p.Link = q
   834  					p = q
   835  				}
   836  			}
   837  
   838  			if p.As != obj.ARET {
   839  				q = newprog()
   840  				q.Pos = p.Pos
   841  				q.Link = p.Link
   842  				p.Link = q
   843  				p = q
   844  			}
   845  
   846  			if retjmp != nil { // retjmp
   847  				p.As = AB
   848  				p.To.Type = obj.TYPE_BRANCH
   849  				p.To.Sym = retjmp
   850  				p.Spadj = +c.autosize
   851  				break
   852  			}
   853  
   854  			p.As = obj.ARET
   855  			p.To.Type = obj.TYPE_MEM
   856  			p.To.Offset = 0
   857  			p.To.Reg = REGLINK
   858  			p.Spadj = +c.autosize
   859  
   860  		case AADD, ASUB:
   861  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   862  				if p.As == AADD {
   863  					p.Spadj = int32(-p.From.Offset)
   864  				} else {
   865  					p.Spadj = int32(+p.From.Offset)
   866  				}
   867  			}
   868  
   869  		case obj.AGETCALLERPC:
   870  			if cursym.Leaf() {
   871  				/* MOVD LR, Rd */
   872  				p.As = AMOVD
   873  				p.From.Type = obj.TYPE_REG
   874  				p.From.Reg = REGLINK
   875  			} else {
   876  				/* MOVD (RSP), Rd */
   877  				p.As = AMOVD
   878  				p.From.Type = obj.TYPE_MEM
   879  				p.From.Reg = REGSP
   880  			}
   881  
   882  		case obj.ADUFFCOPY:
   883  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   884  				//  ADR	ret_addr, R27
   885  				//  STP	(FP, R27), -24(SP)
   886  				//  SUB	24, SP, FP
   887  				//  DUFFCOPY
   888  				// ret_addr:
   889  				//  SUB	8, SP, FP
   890  
   891  				q1 := p
   892  				// copy DUFFCOPY from q1 to q4
   893  				q4 := obj.Appendp(p, c.newprog)
   894  				q4.Pos = p.Pos
   895  				q4.As = obj.ADUFFCOPY
   896  				q4.To = p.To
   897  
   898  				q1.As = AADR
   899  				q1.From.Type = obj.TYPE_BRANCH
   900  				q1.To.Type = obj.TYPE_REG
   901  				q1.To.Reg = REG_R27
   902  
   903  				q2 := obj.Appendp(q1, c.newprog)
   904  				q2.Pos = p.Pos
   905  				q2.As = ASTP
   906  				q2.From.Type = obj.TYPE_REGREG
   907  				q2.From.Reg = REGFP
   908  				q2.From.Offset = int64(REG_R27)
   909  				q2.To.Type = obj.TYPE_MEM
   910  				q2.To.Reg = REGSP
   911  				q2.To.Offset = -24
   912  
   913  				// maintaine FP for DUFFCOPY
   914  				q3 := obj.Appendp(q2, c.newprog)
   915  				q3.Pos = p.Pos
   916  				q3.As = ASUB
   917  				q3.From.Type = obj.TYPE_CONST
   918  				q3.From.Offset = 24
   919  				q3.Reg = REGSP
   920  				q3.To.Type = obj.TYPE_REG
   921  				q3.To.Reg = REGFP
   922  
   923  				q5 := obj.Appendp(q4, c.newprog)
   924  				q5.Pos = p.Pos
   925  				q5.As = ASUB
   926  				q5.From.Type = obj.TYPE_CONST
   927  				q5.From.Offset = 8
   928  				q5.Reg = REGSP
   929  				q5.To.Type = obj.TYPE_REG
   930  				q5.To.Reg = REGFP
   931  				q1.Pcond = q5
   932  				p = q5
   933  			}
   934  
   935  		case obj.ADUFFZERO:
   936  			if objabi.Framepointer_enabled(objabi.GOOS, objabi.GOARCH) {
   937  				//  ADR	ret_addr, R27
   938  				//  STP	(FP, R27), -24(SP)
   939  				//  SUB	24, SP, FP
   940  				//  DUFFZERO
   941  				// ret_addr:
   942  				//  SUB	8, SP, FP
   943  
   944  				q1 := p
   945  				// copy DUFFZERO from q1 to q4
   946  				q4 := obj.Appendp(p, c.newprog)
   947  				q4.Pos = p.Pos
   948  				q4.As = obj.ADUFFZERO
   949  				q4.To = p.To
   950  
   951  				q1.As = AADR
   952  				q1.From.Type = obj.TYPE_BRANCH
   953  				q1.To.Type = obj.TYPE_REG
   954  				q1.To.Reg = REG_R27
   955  
   956  				q2 := obj.Appendp(q1, c.newprog)
   957  				q2.Pos = p.Pos
   958  				q2.As = ASTP
   959  				q2.From.Type = obj.TYPE_REGREG
   960  				q2.From.Reg = REGFP
   961  				q2.From.Offset = int64(REG_R27)
   962  				q2.To.Type = obj.TYPE_MEM
   963  				q2.To.Reg = REGSP
   964  				q2.To.Offset = -24
   965  
   966  				// maintaine FP for DUFFZERO
   967  				q3 := obj.Appendp(q2, c.newprog)
   968  				q3.Pos = p.Pos
   969  				q3.As = ASUB
   970  				q3.From.Type = obj.TYPE_CONST
   971  				q3.From.Offset = 24
   972  				q3.Reg = REGSP
   973  				q3.To.Type = obj.TYPE_REG
   974  				q3.To.Reg = REGFP
   975  
   976  				q5 := obj.Appendp(q4, c.newprog)
   977  				q5.Pos = p.Pos
   978  				q5.As = ASUB
   979  				q5.From.Type = obj.TYPE_CONST
   980  				q5.From.Offset = 8
   981  				q5.Reg = REGSP
   982  				q5.To.Type = obj.TYPE_REG
   983  				q5.To.Reg = REGFP
   984  				q1.Pcond = q5
   985  				p = q5
   986  			}
   987  		}
   988  	}
   989  }
   990  
   991  func nocache(p *obj.Prog) {
   992  	p.Optab = 0
   993  	p.From.Class = 0
   994  	p.To.Class = 0
   995  }
   996  
   997  var unaryDst = map[obj.As]bool{
   998  	AWORD:  true,
   999  	ADWORD: true,
  1000  	ABL:    true,
  1001  	AB:     true,
  1002  	ACLREX: true,
  1003  }
  1004  
  1005  var Linkarm64 = obj.LinkArch{
  1006  	Arch:           sys.ArchARM64,
  1007  	Init:           buildop,
  1008  	Preprocess:     preprocess,
  1009  	Assemble:       span7,
  1010  	Progedit:       progedit,
  1011  	UnaryDst:       unaryDst,
  1012  	DWARFRegisters: ARM64DWARFRegisters,
  1013  }