gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

obj7.go (25598B)


      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 }