obj7.go raw

   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