Source file src/cmd/internal/obj/arm64/obj7.go

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

View as plain text