Home | History | Annotate | Download | only in 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 (a] 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 (a] terzarima.net)
     10 // 	Revisions Copyright  2000-2007 Lucent Technologies Inc. and others
     11 // 	Portions Copyright  2009 The Go Authors. All rights reserved.
     12 //
     13 // Permission is hereby granted, free of charge, to any person obtaining a copy
     14 // of this software and associated documentation files (the "Software"), to deal
     15 // in the Software without restriction, including without limitation the rights
     16 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     17 // copies of the Software, and to permit persons to whom the Software is
     18 // furnished to do so, subject to the following conditions:
     19 //
     20 // The above copyright notice and this permission notice shall be included in
     21 // all copies or substantial portions of the Software.
     22 //
     23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     24 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
     26 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     28 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     29 // THE SOFTWARE.
     30 
     31 package arm64
     32 
     33 import (
     34 	"cmd/internal/obj"
     35 	"cmd/internal/objabi"
     36 	"cmd/internal/sys"
     37 	"math"
     38 )
     39 
     40 var complements = []obj.As{
     41 	AADD:  ASUB,
     42 	AADDW: ASUBW,
     43 	ASUB:  AADD,
     44 	ASUBW: AADDW,
     45 	ACMP:  ACMN,
     46 	ACMPW: ACMNW,
     47 	ACMN:  ACMP,
     48 	ACMNW: ACMPW,
     49 }
     50 
     51 func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
     52 	// MOV	g_stackguard(g), R1
     53 	p = obj.Appendp(p, c.newprog)
     54 
     55 	p.As = AMOVD
     56 	p.From.Type = obj.TYPE_MEM
     57 	p.From.Reg = REGG
     58 	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
     59 	if c.cursym.CFunc() {
     60 		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
     61 	}
     62 	p.To.Type = obj.TYPE_REG
     63 	p.To.Reg = REG_R1
     64 
     65 	q := (*obj.Prog)(nil)
     66 	if framesize <= objabi.StackSmall {
     67 		// small stack: SP < stackguard
     68 		//	MOV	SP, R2
     69 		//	CMP	stackguard, R2
     70 		p = obj.Appendp(p, c.newprog)
     71 
     72 		p.As = AMOVD
     73 		p.From.Type = obj.TYPE_REG
     74 		p.From.Reg = REGSP
     75 		p.To.Type = obj.TYPE_REG
     76 		p.To.Reg = REG_R2
     77 
     78 		p = obj.Appendp(p, c.newprog)
     79 		p.As = ACMP
     80 		p.From.Type = obj.TYPE_REG
     81 		p.From.Reg = REG_R1
     82 		p.Reg = REG_R2
     83 	} else if framesize <= objabi.StackBig {
     84 		// large stack: SP-framesize < stackguard-StackSmall
     85 		//	SUB	$(framesize-StackSmall), SP, R2
     86 		//	CMP	stackguard, R2
     87 		p = obj.Appendp(p, c.newprog)
     88 
     89 		p.As = ASUB
     90 		p.From.Type = obj.TYPE_CONST
     91 		p.From.Offset = int64(framesize) - objabi.StackSmall
     92 		p.Reg = REGSP
     93 		p.To.Type = obj.TYPE_REG
     94 		p.To.Reg = REG_R2
     95 
     96 		p = obj.Appendp(p, c.newprog)
     97 		p.As = ACMP
     98 		p.From.Type = obj.TYPE_REG
     99 		p.From.Reg = REG_R1
    100 		p.Reg = REG_R2
    101 	} else {
    102 		// Such a large stack we need to protect against wraparound
    103 		// if SP is close to zero.
    104 		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
    105 		// The +StackGuard on both sides is required to keep the left side positive:
    106 		// SP is allowed to be slightly below stackguard. See stack.h.
    107 		//	CMP	$StackPreempt, R1
    108 		//	BEQ	label_of_call_to_morestack
    109 		//	ADD	$StackGuard, SP, R2
    110 		//	SUB	R1, R2
    111 		//	MOV	$(framesize+(StackGuard-StackSmall)), R3
    112 		//	CMP	R3, R2
    113 		p = obj.Appendp(p, c.newprog)
    114 
    115 		p.As = ACMP
    116 		p.From.Type = obj.TYPE_CONST
    117 		p.From.Offset = objabi.StackPreempt
    118 		p.Reg = REG_R1
    119 
    120 		p = obj.Appendp(p, c.newprog)
    121 		q = p
    122 		p.As = ABEQ
    123 		p.To.Type = obj.TYPE_BRANCH
    124 
    125 		p = obj.Appendp(p, c.newprog)
    126 		p.As = AADD
    127 		p.From.Type = obj.TYPE_CONST
    128 		p.From.Offset = objabi.StackGuard
    129 		p.Reg = REGSP
    130 		p.To.Type = obj.TYPE_REG
    131 		p.To.Reg = REG_R2
    132 
    133 		p = obj.Appendp(p, c.newprog)
    134 		p.As = ASUB
    135 		p.From.Type = obj.TYPE_REG
    136 		p.From.Reg = REG_R1
    137 		p.To.Type = obj.TYPE_REG
    138 		p.To.Reg = REG_R2
    139 
    140 		p = obj.Appendp(p, c.newprog)
    141 		p.As = AMOVD
    142 		p.From.Type = obj.TYPE_CONST
    143 		p.From.Offset = int64(framesize) + (objabi.StackGuard - objabi.StackSmall)
    144 		p.To.Type = obj.TYPE_REG
    145 		p.To.Reg = REG_R3
    146 
    147 		p = obj.Appendp(p, c.newprog)
    148 		p.As = ACMP
    149 		p.From.Type = obj.TYPE_REG
    150 		p.From.Reg = REG_R3
    151 		p.Reg = REG_R2
    152 	}
    153 
    154 	// BLS	do-morestack
    155 	bls := obj.Appendp(p, c.newprog)
    156 	bls.As = ABLS
    157 	bls.To.Type = obj.TYPE_BRANCH
    158 
    159 	var last *obj.Prog
    160 	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
    161 	}
    162 
    163 	// Now we are at the end of the function, but logically
    164 	// we are still in function prologue. We need to fix the
    165 	// SP data and PCDATA.
    166 	spfix := obj.Appendp(last, c.newprog)
    167 	spfix.As = obj.ANOP
    168 	spfix.Spadj = -framesize
    169 
    170 	pcdata := obj.Appendp(spfix, c.newprog)
    171 	pcdata.Pos = c.cursym.Func.Text.Pos
    172 	pcdata.As = obj.APCDATA
    173 	pcdata.From.Type = obj.TYPE_CONST
    174 	pcdata.From.Offset = objabi.PCDATA_StackMapIndex
    175 	pcdata.To.Type = obj.TYPE_CONST
    176 	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
    177 
    178 	// MOV	LR, R3
    179 	movlr := obj.Appendp(pcdata, c.newprog)
    180 	movlr.As = AMOVD
    181 	movlr.From.Type = obj.TYPE_REG
    182 	movlr.From.Reg = REGLINK
    183 	movlr.To.Type = obj.TYPE_REG
    184 	movlr.To.Reg = REG_R3
    185 	if q != nil {
    186 		q.Pcond = movlr
    187 	}
    188 	bls.Pcond = movlr
    189 
    190 	debug := movlr
    191 	if false {
    192 		debug = obj.Appendp(debug, c.newprog)
    193 		debug.As = AMOVD
    194 		debug.From.Type = obj.TYPE_CONST
    195 		debug.From.Offset = int64(framesize)
    196 		debug.To.Type = obj.TYPE_REG
    197 		debug.To.Reg = REGTMP
    198 	}
    199 
    200 	// BL	runtime.morestack(SB)
    201 	call := obj.Appendp(debug, c.newprog)
    202 	call.As = ABL
    203 	call.To.Type = obj.TYPE_BRANCH
    204 	morestack := "runtime.morestack"
    205 	switch {
    206 	case c.cursym.CFunc():
    207 		morestack = "runtime.morestackc"
    208 	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
    209 		morestack = "runtime.morestack_noctxt"
    210 	}
    211 	call.To.Sym = c.ctxt.Lookup(morestack)
    212 
    213 	// B	start
    214 	jmp := obj.Appendp(call, c.newprog)
    215 	jmp.As = AB
    216 	jmp.To.Type = obj.TYPE_BRANCH
    217 	jmp.Pcond = c.cursym.Func.Text.Link
    218 	jmp.Spadj = +framesize
    219 
    220 	// placeholder for bls's jump target
    221 	// p = obj.Appendp(ctxt, p)
    222 	// p.As = obj.ANOP
    223 
    224 	return bls
    225 }
    226 
    227 func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
    228 	c := ctxt7{ctxt: ctxt, newprog: newprog}
    229 
    230 	p.From.Class = 0
    231 	p.To.Class = 0
    232 
    233 	// $0 results in C_ZCON, which matches both C_REG and various
    234 	// C_xCON, however the C_REG cases in asmout don't expect a
    235 	// constant, so they will use the register fields and assemble
    236 	// a R0. To prevent that, rewrite $0 as ZR.
    237 	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
    238 		p.From.Type = obj.TYPE_REG
    239 		p.From.Reg = REGZERO
    240 	}
    241 	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
    242 		p.To.Type = obj.TYPE_REG
    243 		p.To.Reg = REGZERO
    244 	}
    245 
    246 	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    247 	switch p.As {
    248 	case AB,
    249 		ABL,
    250 		obj.ARET,
    251 		obj.ADUFFZERO,
    252 		obj.ADUFFCOPY:
    253 		if p.To.Sym != nil {
    254 			p.To.Type = obj.TYPE_BRANCH
    255 		}
    256 		break
    257 	}
    258 
    259 	// Rewrite float constants to values stored in memory.
    260 	switch p.As {
    261 	case AFMOVS:
    262 		if p.From.Type == obj.TYPE_FCONST {
    263 			f32 := float32(p.From.Val.(float64))
    264 			if math.Float32bits(f32) == 0 {
    265 				p.From.Type = obj.TYPE_REG
    266 				p.From.Reg = REGZERO
    267 				break
    268 			}
    269 			p.From.Type = obj.TYPE_MEM
    270 			p.From.Sym = c.ctxt.Float32Sym(f32)
    271 			p.From.Name = obj.NAME_EXTERN
    272 			p.From.Offset = 0
    273 		}
    274 
    275 	case AFMOVD:
    276 		if p.From.Type == obj.TYPE_FCONST {
    277 			f64 := p.From.Val.(float64)
    278 			if math.Float64bits(f64) == 0 {
    279 				p.From.Type = obj.TYPE_REG
    280 				p.From.Reg = REGZERO
    281 				break
    282 			}
    283 			p.From.Type = obj.TYPE_MEM
    284 			p.From.Sym = c.ctxt.Float64Sym(f64)
    285 			p.From.Name = obj.NAME_EXTERN
    286 			p.From.Offset = 0
    287 		}
    288 
    289 		break
    290 	}
    291 
    292 	// Rewrite negative immediates as positive immediates with
    293 	// complementary instruction.
    294 	switch p.As {
    295 	case AADD, ASUB, ACMP, ACMN:
    296 		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
    297 			p.From.Offset = -p.From.Offset
    298 			p.As = complements[p.As]
    299 		}
    300 	case AADDW, ASUBW, ACMPW, ACMNW:
    301 		if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
    302 			p.From.Offset = -p.From.Offset
    303 			p.As = complements[p.As]
    304 		}
    305 	}
    306 
    307 	// For 32-bit logical instruction with constant,
    308 	// rewrite the high 32-bit to be a repetition of
    309 	// the low 32-bit, so that the BITCON test can be
    310 	// shared for both 32-bit and 64-bit. 32-bit ops
    311 	// will zero the high 32-bit of the destination
    312 	// register anyway.
    313 	switch p.As {
    314 	case AANDW, AORRW, AEORW, AANDSW:
    315 		if p.From.Type == obj.TYPE_CONST {
    316 			v := p.From.Offset & 0xffffffff
    317 			p.From.Offset = v | v<<32
    318 		}
    319 	}
    320 
    321 	if c.ctxt.Flag_dynlink {
    322 		c.rewriteToUseGot(p)
    323 	}
    324 }
    325 
    326 // Rewrite p, if necessary, to access global data via the global offset table.
    327 func (c *ctxt7) rewriteToUseGot(p *obj.Prog) {
    328 	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
    329 		//     ADUFFxxx $offset
    330 		// becomes
    331 		//     MOVD runtime.duffxxx@GOT, REGTMP
    332 		//     ADD $offset, REGTMP
    333 		//     CALL REGTMP
    334 		var sym *obj.LSym
    335 		if p.As == obj.ADUFFZERO {
    336 			sym = c.ctxt.Lookup("runtime.duffzero")
    337 		} else {
    338 			sym = c.ctxt.Lookup("runtime.duffcopy")
    339 		}
    340 		offset := p.To.Offset
    341 		p.As = AMOVD
    342 		p.From.Type = obj.TYPE_MEM
    343 		p.From.Name = obj.NAME_GOTREF
    344 		p.From.Sym = sym
    345 		p.To.Type = obj.TYPE_REG
    346 		p.To.Reg = REGTMP
    347 		p.To.Name = obj.NAME_NONE
    348 		p.To.Offset = 0
    349 		p.To.Sym = nil
    350 		p1 := obj.Appendp(p, c.newprog)
    351 		p1.As = AADD
    352 		p1.From.Type = obj.TYPE_CONST
    353 		p1.From.Offset = offset
    354 		p1.To.Type = obj.TYPE_REG
    355 		p1.To.Reg = REGTMP
    356 		p2 := obj.Appendp(p1, c.newprog)
    357 		p2.As = obj.ACALL
    358 		p2.To.Type = obj.TYPE_REG
    359 		p2.To.Reg = REGTMP
    360 	}
    361 
    362 	// We only care about global data: NAME_EXTERN means a global
    363 	// symbol in the Go sense, and p.Sym.Local is true for a few
    364 	// internally defined symbols.
    365 	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    366 		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
    367 		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
    368 		if p.As != AMOVD {
    369 			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
    370 		}
    371 		if p.To.Type != obj.TYPE_REG {
    372 			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
    373 		}
    374 		p.From.Type = obj.TYPE_MEM
    375 		p.From.Name = obj.NAME_GOTREF
    376 		if p.From.Offset != 0 {
    377 			q := obj.Appendp(p, c.newprog)
    378 			q.As = AADD
    379 			q.From.Type = obj.TYPE_CONST
    380 			q.From.Offset = p.From.Offset
    381 			q.To = p.To
    382 			p.From.Offset = 0
    383 		}
    384 	}
    385 	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
    386 		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
    387 	}
    388 	var source *obj.Addr
    389 	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
    390 	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
    391 	// An addition may be inserted between the two MOVs if there is an offset.
    392 	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    393 		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    394 			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    395 		}
    396 		source = &p.From
    397 	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    398 		source = &p.To
    399 	} else {
    400 		return
    401 	}
    402 	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    403 		return
    404 	}
    405 	if source.Sym.Type == objabi.STLSBSS {
    406 		return
    407 	}
    408 	if source.Type != obj.TYPE_MEM {
    409 		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
    410 	}
    411 	p1 := obj.Appendp(p, c.newprog)
    412 	p2 := obj.Appendp(p1, c.newprog)
    413 	p1.As = AMOVD
    414 	p1.From.Type = obj.TYPE_MEM
    415 	p1.From.Sym = source.Sym
    416 	p1.From.Name = obj.NAME_GOTREF
    417 	p1.To.Type = obj.TYPE_REG
    418 	p1.To.Reg = REGTMP
    419 
    420 	p2.As = p.As
    421 	p2.From = p.From
    422 	p2.To = p.To
    423 	if p.From.Name == obj.NAME_EXTERN {
    424 		p2.From.Reg = REGTMP
    425 		p2.From.Name = obj.NAME_NONE
    426 		p2.From.Sym = nil
    427 	} else if p.To.Name == obj.NAME_EXTERN {
    428 		p2.To.Reg = REGTMP
    429 		p2.To.Name = obj.NAME_NONE
    430 		p2.To.Sym = nil
    431 	} else {
    432 		return
    433 	}
    434 	obj.Nopout(p)
    435 }
    436 
    437 func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
    438 	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
    439 		return
    440 	}
    441 
    442 	c := ctxt7{ctxt: ctxt, newprog: newprog, cursym: cursym}
    443 
    444 	p := c.cursym.Func.Text
    445 	textstksiz := p.To.Offset
    446 	aoffset := int32(textstksiz)
    447 
    448 	c.cursym.Func.Args = p.To.Val.(int32)
    449 	c.cursym.Func.Locals = int32(textstksiz)
    450 
    451 	/*
    452 	 * find leaf subroutines
    453 	 * strip NOPs
    454 	 * expand RET
    455 	 */
    456 	q := (*obj.Prog)(nil)
    457 	var q1 *obj.Prog
    458 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
    459 		switch p.As {
    460 		case obj.ATEXT:
    461 			p.Mark |= LEAF
    462 
    463 		case obj.ARET:
    464 			break
    465 
    466 		case obj.ANOP:
    467 			q1 = p.Link
    468 			q.Link = q1 /* q is non-nop */
    469 			q1.Mark |= p.Mark
    470 			continue
    471 
    472 		case ABL,
    473 			obj.ADUFFZERO,
    474 			obj.ADUFFCOPY:
    475 			c.cursym.Func.Text.Mark &^= LEAF
    476 			fallthrough
    477 
    478 		case ACBNZ,
    479 			ACBZ,
    480 			ACBNZW,
    481 			ACBZW,
    482 			ATBZ,
    483 			ATBNZ,
    484 			AB,
    485 			ABEQ,
    486 			ABNE,
    487 			ABCS,
    488 			ABHS,
    489 			ABCC,
    490 			ABLO,
    491 			ABMI,
    492 			ABPL,
    493 			ABVS,
    494 			ABVC,
    495 			ABHI,
    496 			ABLS,
    497 			ABGE,
    498 			ABLT,
    499 			ABGT,
    500 			ABLE,
    501 			AADR, /* strange */
    502 			AADRP:
    503 			q1 = p.Pcond
    504 
    505 			if q1 != nil {
    506 				for q1.As == obj.ANOP {
    507 					q1 = q1.Link
    508 					p.Pcond = q1
    509 				}
    510 			}
    511 
    512 			break
    513 		}
    514 
    515 		q = p
    516 	}
    517 
    518 	var retjmp *obj.LSym
    519 	for p := c.cursym.Func.Text; p != nil; p = p.Link {
    520 		o := p.As
    521 		switch o {
    522 		case obj.ATEXT:
    523 			c.cursym.Func.Text = p
    524 			if textstksiz < 0 {
    525 				c.autosize = 0
    526 			} else {
    527 				c.autosize = int32(textstksiz + 8)
    528 			}
    529 			if (c.cursym.Func.Text.Mark&LEAF != 0) && c.autosize <= 8 {
    530 				c.autosize = 0
    531 			} else if c.autosize&(16-1) != 0 {
    532 				// The frame includes an LR.
    533 				// If the frame size is 8, it's only an LR,
    534 				// so there's no potential for breaking references to
    535 				// local variables by growing the frame size,
    536 				// because there are no local variables.
    537 				// But otherwise, if there is a non-empty locals section,
    538 				// the author of the code is responsible for making sure
    539 				// that the frame size is 8 mod 16.
    540 				if c.autosize == 8 {
    541 					c.autosize += 8
    542 					c.cursym.Func.Locals += 8
    543 				} else {
    544 					c.ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, c.autosize-8)
    545 				}
    546 			}
    547 			p.To.Offset = int64(c.autosize) - 8
    548 			if c.autosize == 0 && !(c.cursym.Func.Text.Mark&LEAF != 0) {
    549 				if c.ctxt.Debugvlog {
    550 					c.ctxt.Logf("save suppressed in: %s\n", c.cursym.Func.Text.From.Sym.Name)
    551 				}
    552 				c.cursym.Func.Text.Mark |= LEAF
    553 			}
    554 
    555 			if !p.From.Sym.NoSplit() {
    556 				p = c.stacksplit(p, c.autosize) // emit split check
    557 			}
    558 
    559 			aoffset = c.autosize
    560 			if aoffset > 0xF0 {
    561 				aoffset = 0xF0
    562 			}
    563 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    564 				c.cursym.Set(obj.AttrLeaf, true)
    565 				if c.autosize == 0 {
    566 					break
    567 				}
    568 			}
    569 
    570 			// Frame is non-empty. Make sure to save link register, even if
    571 			// it is a leaf function, so that traceback works.
    572 			q = p
    573 			if c.autosize > aoffset {
    574 				// Frame size is too large for a MOVD.W instruction.
    575 				// Store link register before decrementing SP, so if a signal comes
    576 				// during the execution of the function prologue, the traceback
    577 				// code will not see a half-updated stack frame.
    578 				q = obj.Appendp(q, c.newprog)
    579 				q.Pos = p.Pos
    580 				q.As = ASUB
    581 				q.From.Type = obj.TYPE_CONST
    582 				q.From.Offset = int64(c.autosize)
    583 				q.Reg = REGSP
    584 				q.To.Type = obj.TYPE_REG
    585 				q.To.Reg = REGTMP
    586 
    587 				q = obj.Appendp(q, c.newprog)
    588 				q.Pos = p.Pos
    589 				q.As = AMOVD
    590 				q.From.Type = obj.TYPE_REG
    591 				q.From.Reg = REGLINK
    592 				q.To.Type = obj.TYPE_MEM
    593 				q.To.Reg = REGTMP
    594 
    595 				q1 = obj.Appendp(q, c.newprog)
    596 				q1.Pos = p.Pos
    597 				q1.As = AMOVD
    598 				q1.From.Type = obj.TYPE_REG
    599 				q1.From.Reg = REGTMP
    600 				q1.To.Type = obj.TYPE_REG
    601 				q1.To.Reg = REGSP
    602 				q1.Spadj = c.autosize
    603 			} else {
    604 				// small frame, update SP and save LR in a single MOVD.W instruction
    605 				q1 = obj.Appendp(q, c.newprog)
    606 				q1.As = AMOVD
    607 				q1.Pos = p.Pos
    608 				q1.From.Type = obj.TYPE_REG
    609 				q1.From.Reg = REGLINK
    610 				q1.To.Type = obj.TYPE_MEM
    611 				q1.Scond = C_XPRE
    612 				q1.To.Offset = int64(-aoffset)
    613 				q1.To.Reg = REGSP
    614 				q1.Spadj = aoffset
    615 			}
    616 
    617 			if c.cursym.Func.Text.From.Sym.Wrapper() {
    618 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    619 				//
    620 				//	MOV  g_panic(g), R1
    621 				//	CBNZ checkargp
    622 				// end:
    623 				//	NOP
    624 				// ... function body ...
    625 				// checkargp:
    626 				//	MOV  panic_argp(R1), R2
    627 				//	ADD  $(autosize+8), RSP, R3
    628 				//	CMP  R2, R3
    629 				//	BNE  end
    630 				//	ADD  $8, RSP, R4
    631 				//	MOVD R4, panic_argp(R1)
    632 				//	B    end
    633 				//
    634 				// The NOP is needed to give the jumps somewhere to land.
    635 				// It is a liblink NOP, not an ARM64 NOP: it encodes to 0 instruction bytes.
    636 				q = q1
    637 
    638 				// MOV g_panic(g), R1
    639 				q = obj.Appendp(q, c.newprog)
    640 				q.As = AMOVD
    641 				q.From.Type = obj.TYPE_MEM
    642 				q.From.Reg = REGG
    643 				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
    644 				q.To.Type = obj.TYPE_REG
    645 				q.To.Reg = REG_R1
    646 
    647 				// CBNZ R1, checkargp
    648 				cbnz := obj.Appendp(q, c.newprog)
    649 				cbnz.As = ACBNZ
    650 				cbnz.From.Type = obj.TYPE_REG
    651 				cbnz.From.Reg = REG_R1
    652 				cbnz.To.Type = obj.TYPE_BRANCH
    653 
    654 				// Empty branch target at the top of the function body
    655 				end := obj.Appendp(cbnz, c.newprog)
    656 				end.As = obj.ANOP
    657 
    658 				// find the end of the function
    659 				var last *obj.Prog
    660 				for last = end; last.Link != nil; last = last.Link {
    661 				}
    662 
    663 				// MOV panic_argp(R1), R2
    664 				mov := obj.Appendp(last, c.newprog)
    665 				mov.As = AMOVD
    666 				mov.From.Type = obj.TYPE_MEM
    667 				mov.From.Reg = REG_R1
    668 				mov.From.Offset = 0 // Panic.argp
    669 				mov.To.Type = obj.TYPE_REG
    670 				mov.To.Reg = REG_R2
    671 
    672 				// CBNZ branches to the MOV above
    673 				cbnz.Pcond = mov
    674 
    675 				// ADD $(autosize+8), SP, R3
    676 				q = obj.Appendp(mov, c.newprog)
    677 				q.As = AADD
    678 				q.From.Type = obj.TYPE_CONST
    679 				q.From.Offset = int64(c.autosize) + 8
    680 				q.Reg = REGSP
    681 				q.To.Type = obj.TYPE_REG
    682 				q.To.Reg = REG_R3
    683 
    684 				// CMP R2, R3
    685 				q = obj.Appendp(q, c.newprog)
    686 				q.As = ACMP
    687 				q.From.Type = obj.TYPE_REG
    688 				q.From.Reg = REG_R2
    689 				q.Reg = REG_R3
    690 
    691 				// BNE end
    692 				q = obj.Appendp(q, c.newprog)
    693 				q.As = ABNE
    694 				q.To.Type = obj.TYPE_BRANCH
    695 				q.Pcond = end
    696 
    697 				// ADD $8, SP, R4
    698 				q = obj.Appendp(q, c.newprog)
    699 				q.As = AADD
    700 				q.From.Type = obj.TYPE_CONST
    701 				q.From.Offset = 8
    702 				q.Reg = REGSP
    703 				q.To.Type = obj.TYPE_REG
    704 				q.To.Reg = REG_R4
    705 
    706 				// MOV R4, panic_argp(R1)
    707 				q = obj.Appendp(q, c.newprog)
    708 				q.As = AMOVD
    709 				q.From.Type = obj.TYPE_REG
    710 				q.From.Reg = REG_R4
    711 				q.To.Type = obj.TYPE_MEM
    712 				q.To.Reg = REG_R1
    713 				q.To.Offset = 0 // Panic.argp
    714 
    715 				// B end
    716 				q = obj.Appendp(q, c.newprog)
    717 				q.As = AB
    718 				q.To.Type = obj.TYPE_BRANCH
    719 				q.Pcond = end
    720 			}
    721 
    722 		case obj.ARET:
    723 			nocache(p)
    724 			if p.From.Type == obj.TYPE_CONST {
    725 				c.ctxt.Diag("using BECOME (%v) is not supported!", p)
    726 				break
    727 			}
    728 
    729 			retjmp = p.To.Sym
    730 			p.To = obj.Addr{}
    731 			if c.cursym.Func.Text.Mark&LEAF != 0 {
    732 				if c.autosize != 0 {
    733 					p.As = AADD
    734 					p.From.Type = obj.TYPE_CONST
    735 					p.From.Offset = int64(c.autosize)
    736 					p.To.Type = obj.TYPE_REG
    737 					p.To.Reg = REGSP
    738 					p.Spadj = -c.autosize
    739 				}
    740 			} else {
    741 				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
    742 				aoffset = c.autosize
    743 
    744 				if aoffset > 0xF0 {
    745 					aoffset = 0xF0
    746 				}
    747 				p.As = AMOVD
    748 				p.From.Type = obj.TYPE_MEM
    749 				p.Scond = C_XPOST
    750 				p.From.Offset = int64(aoffset)
    751 				p.From.Reg = REGSP
    752 				p.To.Type = obj.TYPE_REG
    753 				p.To.Reg = REGLINK
    754 				p.Spadj = -aoffset
    755 				if c.autosize > aoffset {
    756 					q = newprog()
    757 					q.As = AADD
    758 					q.From.Type = obj.TYPE_CONST
    759 					q.From.Offset = int64(c.autosize) - int64(aoffset)
    760 					q.To.Type = obj.TYPE_REG
    761 					q.To.Reg = REGSP
    762 					q.Link = p.Link
    763 					q.Spadj = int32(-q.From.Offset)
    764 					q.Pos = p.Pos
    765 					p.Link = q
    766 					p = q
    767 				}
    768 			}
    769 
    770 			if p.As != obj.ARET {
    771 				q = newprog()
    772 				q.Pos = p.Pos
    773 				q.Link = p.Link
    774 				p.Link = q
    775 				p = q
    776 			}
    777 
    778 			if retjmp != nil { // retjmp
    779 				p.As = AB
    780 				p.To.Type = obj.TYPE_BRANCH
    781 				p.To.Sym = retjmp
    782 				p.Spadj = +c.autosize
    783 				break
    784 			}
    785 
    786 			p.As = obj.ARET
    787 			p.To.Type = obj.TYPE_MEM
    788 			p.To.Offset = 0
    789 			p.To.Reg = REGLINK
    790 			p.Spadj = +c.autosize
    791 
    792 		case AADD, ASUB:
    793 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    794 				if p.As == AADD {
    795 					p.Spadj = int32(-p.From.Offset)
    796 				} else {
    797 					p.Spadj = int32(+p.From.Offset)
    798 				}
    799 			}
    800 			break
    801 		}
    802 	}
    803 }
    804 
    805 func nocache(p *obj.Prog) {
    806 	p.Optab = 0
    807 	p.From.Class = 0
    808 	p.To.Class = 0
    809 }
    810 
    811 var unaryDst = map[obj.As]bool{
    812 	AWORD:  true,
    813 	ADWORD: true,
    814 	ABL:    true,
    815 	AB:     true,
    816 	ACLREX: true,
    817 }
    818 
    819 var Linkarm64 = obj.LinkArch{
    820 	Arch:       sys.ArchARM64,
    821 	Init:       buildop,
    822 	Preprocess: preprocess,
    823 	Assemble:   span7,
    824 	Progedit:   progedit,
    825 	UnaryDst:   unaryDst,
    826 }
    827