Black Lives Matter. Support the Equal Justice Initiative.

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

Documentation: cmd/internal/obj/arm64

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

View as plain text