...

Source file src/github.com/twitchyliquid64/golang-asm/obj/arm64/obj7.go

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

View as plain text