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/sys"
     36 	"fmt"
     37 	"log"
     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 stacksplit(ctxt *obj.Link, p *obj.Prog, framesize int32) *obj.Prog {
     53 	// MOV	g_stackguard(g), R1
     54 	p = obj.Appendp(ctxt, p)
     55 
     56 	p.As = AMOVD
     57 	p.From.Type = obj.TYPE_MEM
     58 	p.From.Reg = REGG
     59 	p.From.Offset = 2 * int64(ctxt.Arch.PtrSize) // G.stackguard0
     60 	if ctxt.Cursym.CFunc() {
     61 		p.From.Offset = 3 * int64(ctxt.Arch.PtrSize) // G.stackguard1
     62 	}
     63 	p.To.Type = obj.TYPE_REG
     64 	p.To.Reg = REG_R1
     65 
     66 	q := (*obj.Prog)(nil)
     67 	if framesize <= obj.StackSmall {
     68 		// small stack: SP < stackguard
     69 		//	MOV	SP, R2
     70 		//	CMP	stackguard, R2
     71 		p = obj.Appendp(ctxt, p)
     72 
     73 		p.As = AMOVD
     74 		p.From.Type = obj.TYPE_REG
     75 		p.From.Reg = REGSP
     76 		p.To.Type = obj.TYPE_REG
     77 		p.To.Reg = REG_R2
     78 
     79 		p = obj.Appendp(ctxt, p)
     80 		p.As = ACMP
     81 		p.From.Type = obj.TYPE_REG
     82 		p.From.Reg = REG_R1
     83 		p.Reg = REG_R2
     84 	} else if framesize <= obj.StackBig {
     85 		// large stack: SP-framesize < stackguard-StackSmall
     86 		//	SUB	$framesize, SP, R2
     87 		//	CMP	stackguard, R2
     88 		p = obj.Appendp(ctxt, p)
     89 
     90 		p.As = ASUB
     91 		p.From.Type = obj.TYPE_CONST
     92 		p.From.Offset = int64(framesize)
     93 		p.Reg = REGSP
     94 		p.To.Type = obj.TYPE_REG
     95 		p.To.Reg = REG_R2
     96 
     97 		p = obj.Appendp(ctxt, p)
     98 		p.As = ACMP
     99 		p.From.Type = obj.TYPE_REG
    100 		p.From.Reg = REG_R1
    101 		p.Reg = REG_R2
    102 	} else {
    103 		// Such a large stack we need to protect against wraparound
    104 		// if SP is close to zero.
    105 		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
    106 		// The +StackGuard on both sides is required to keep the left side positive:
    107 		// SP is allowed to be slightly below stackguard. See stack.h.
    108 		//	CMP	$StackPreempt, R1
    109 		//	BEQ	label_of_call_to_morestack
    110 		//	ADD	$StackGuard, SP, R2
    111 		//	SUB	R1, R2
    112 		//	MOV	$(framesize+(StackGuard-StackSmall)), R3
    113 		//	CMP	R3, R2
    114 		p = obj.Appendp(ctxt, p)
    115 
    116 		p.As = ACMP
    117 		p.From.Type = obj.TYPE_CONST
    118 		p.From.Offset = obj.StackPreempt
    119 		p.Reg = REG_R1
    120 
    121 		p = obj.Appendp(ctxt, p)
    122 		q = p
    123 		p.As = ABEQ
    124 		p.To.Type = obj.TYPE_BRANCH
    125 
    126 		p = obj.Appendp(ctxt, p)
    127 		p.As = AADD
    128 		p.From.Type = obj.TYPE_CONST
    129 		p.From.Offset = obj.StackGuard
    130 		p.Reg = REGSP
    131 		p.To.Type = obj.TYPE_REG
    132 		p.To.Reg = REG_R2
    133 
    134 		p = obj.Appendp(ctxt, p)
    135 		p.As = ASUB
    136 		p.From.Type = obj.TYPE_REG
    137 		p.From.Reg = REG_R1
    138 		p.To.Type = obj.TYPE_REG
    139 		p.To.Reg = REG_R2
    140 
    141 		p = obj.Appendp(ctxt, p)
    142 		p.As = AMOVD
    143 		p.From.Type = obj.TYPE_CONST
    144 		p.From.Offset = int64(framesize) + (obj.StackGuard - obj.StackSmall)
    145 		p.To.Type = obj.TYPE_REG
    146 		p.To.Reg = REG_R3
    147 
    148 		p = obj.Appendp(ctxt, p)
    149 		p.As = ACMP
    150 		p.From.Type = obj.TYPE_REG
    151 		p.From.Reg = REG_R3
    152 		p.Reg = REG_R2
    153 	}
    154 
    155 	// BLS	do-morestack
    156 	bls := obj.Appendp(ctxt, p)
    157 	bls.As = ABLS
    158 	bls.To.Type = obj.TYPE_BRANCH
    159 
    160 	var last *obj.Prog
    161 	for last = ctxt.Cursym.Text; last.Link != nil; last = last.Link {
    162 	}
    163 
    164 	// Now we are at the end of the function, but logically
    165 	// we are still in function prologue. We need to fix the
    166 	// SP data and PCDATA.
    167 	spfix := obj.Appendp(ctxt, last)
    168 	spfix.As = obj.ANOP
    169 	spfix.Spadj = -framesize
    170 
    171 	pcdata := obj.Appendp(ctxt, spfix)
    172 	pcdata.Lineno = ctxt.Cursym.Text.Lineno
    173 	pcdata.Mode = ctxt.Cursym.Text.Mode
    174 	pcdata.As = obj.APCDATA
    175 	pcdata.From.Type = obj.TYPE_CONST
    176 	pcdata.From.Offset = obj.PCDATA_StackMapIndex
    177 	pcdata.To.Type = obj.TYPE_CONST
    178 	pcdata.To.Offset = -1 // pcdata starts at -1 at function entry
    179 
    180 	// MOV	LR, R3
    181 	movlr := obj.Appendp(ctxt, pcdata)
    182 	movlr.As = AMOVD
    183 	movlr.From.Type = obj.TYPE_REG
    184 	movlr.From.Reg = REGLINK
    185 	movlr.To.Type = obj.TYPE_REG
    186 	movlr.To.Reg = REG_R3
    187 	if q != nil {
    188 		q.Pcond = movlr
    189 	}
    190 	bls.Pcond = movlr
    191 
    192 	debug := movlr
    193 	if false {
    194 		debug = obj.Appendp(ctxt, debug)
    195 		debug.As = AMOVD
    196 		debug.From.Type = obj.TYPE_CONST
    197 		debug.From.Offset = int64(framesize)
    198 		debug.To.Type = obj.TYPE_REG
    199 		debug.To.Reg = REGTMP
    200 	}
    201 
    202 	// BL	runtime.morestack(SB)
    203 	call := obj.Appendp(ctxt, debug)
    204 	call.As = ABL
    205 	call.To.Type = obj.TYPE_BRANCH
    206 	morestack := "runtime.morestack"
    207 	switch {
    208 	case ctxt.Cursym.CFunc():
    209 		morestack = "runtime.morestackc"
    210 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
    211 		morestack = "runtime.morestack_noctxt"
    212 	}
    213 	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
    214 
    215 	// B	start
    216 	jmp := obj.Appendp(ctxt, call)
    217 	jmp.As = AB
    218 	jmp.To.Type = obj.TYPE_BRANCH
    219 	jmp.Pcond = ctxt.Cursym.Text.Link
    220 	jmp.Spadj = +framesize
    221 
    222 	// placeholder for bls's jump target
    223 	// p = obj.Appendp(ctxt, p)
    224 	// p.As = obj.ANOP
    225 
    226 	return bls
    227 }
    228 
    229 func progedit(ctxt *obj.Link, p *obj.Prog) {
    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 			i32 := math.Float32bits(f32)
    265 			if i32 == 0 {
    266 				p.From.Type = obj.TYPE_REG
    267 				p.From.Reg = REGZERO
    268 				break
    269 			}
    270 			literal := fmt.Sprintf("$f32.%08x", i32)
    271 			s := obj.Linklookup(ctxt, literal, 0)
    272 			s.Size = 4
    273 			p.From.Type = obj.TYPE_MEM
    274 			p.From.Sym = s
    275 			p.From.Sym.Set(obj.AttrLocal, true)
    276 			p.From.Name = obj.NAME_EXTERN
    277 			p.From.Offset = 0
    278 		}
    279 
    280 	case AFMOVD:
    281 		if p.From.Type == obj.TYPE_FCONST {
    282 			i64 := math.Float64bits(p.From.Val.(float64))
    283 			if i64 == 0 {
    284 				p.From.Type = obj.TYPE_REG
    285 				p.From.Reg = REGZERO
    286 				break
    287 			}
    288 			literal := fmt.Sprintf("$f64.%016x", i64)
    289 			s := obj.Linklookup(ctxt, literal, 0)
    290 			s.Size = 8
    291 			p.From.Type = obj.TYPE_MEM
    292 			p.From.Sym = s
    293 			p.From.Sym.Set(obj.AttrLocal, true)
    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 	switch p.As {
    323 	case AANDW, AORRW, AEORW, AANDSW:
    324 		if p.From.Type == obj.TYPE_CONST {
    325 			v := p.From.Offset & 0xffffffff
    326 			p.From.Offset = v | v<<32
    327 		}
    328 	}
    329 
    330 	if ctxt.Flag_dynlink {
    331 		rewriteToUseGot(ctxt, p)
    332 	}
    333 }
    334 
    335 // Rewrite p, if necessary, to access global data via the global offset table.
    336 func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
    337 	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
    338 		//     ADUFFxxx $offset
    339 		// becomes
    340 		//     MOVD runtime.duffxxx@GOT, REGTMP
    341 		//     ADD $offset, REGTMP
    342 		//     CALL REGTMP
    343 		var sym *obj.LSym
    344 		if p.As == obj.ADUFFZERO {
    345 			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
    346 		} else {
    347 			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
    348 		}
    349 		offset := p.To.Offset
    350 		p.As = AMOVD
    351 		p.From.Type = obj.TYPE_MEM
    352 		p.From.Name = obj.NAME_GOTREF
    353 		p.From.Sym = sym
    354 		p.To.Type = obj.TYPE_REG
    355 		p.To.Reg = REGTMP
    356 		p.To.Name = obj.NAME_NONE
    357 		p.To.Offset = 0
    358 		p.To.Sym = nil
    359 		p1 := obj.Appendp(ctxt, p)
    360 		p1.As = AADD
    361 		p1.From.Type = obj.TYPE_CONST
    362 		p1.From.Offset = offset
    363 		p1.To.Type = obj.TYPE_REG
    364 		p1.To.Reg = REGTMP
    365 		p2 := obj.Appendp(ctxt, p1)
    366 		p2.As = obj.ACALL
    367 		p2.To.Type = obj.TYPE_REG
    368 		p2.To.Reg = REGTMP
    369 	}
    370 
    371 	// We only care about global data: NAME_EXTERN means a global
    372 	// symbol in the Go sense, and p.Sym.Local is true for a few
    373 	// internally defined symbols.
    374 	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    375 		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
    376 		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
    377 		if p.As != AMOVD {
    378 			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
    379 		}
    380 		if p.To.Type != obj.TYPE_REG {
    381 			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
    382 		}
    383 		p.From.Type = obj.TYPE_MEM
    384 		p.From.Name = obj.NAME_GOTREF
    385 		if p.From.Offset != 0 {
    386 			q := obj.Appendp(ctxt, p)
    387 			q.As = AADD
    388 			q.From.Type = obj.TYPE_CONST
    389 			q.From.Offset = p.From.Offset
    390 			q.To = p.To
    391 			p.From.Offset = 0
    392 		}
    393 	}
    394 	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
    395 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    396 	}
    397 	var source *obj.Addr
    398 	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
    399 	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
    400 	// An addition may be inserted between the two MOVs if there is an offset.
    401 	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
    402 		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    403 			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
    404 		}
    405 		source = &p.From
    406 	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
    407 		source = &p.To
    408 	} else {
    409 		return
    410 	}
    411 	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
    412 		return
    413 	}
    414 	if source.Sym.Type == obj.STLSBSS {
    415 		return
    416 	}
    417 	if source.Type != obj.TYPE_MEM {
    418 		ctxt.Diag("don't know how to handle %v with -dynlink", p)
    419 	}
    420 	p1 := obj.Appendp(ctxt, p)
    421 	p2 := obj.Appendp(ctxt, p1)
    422 	p1.As = AMOVD
    423 	p1.From.Type = obj.TYPE_MEM
    424 	p1.From.Sym = source.Sym
    425 	p1.From.Name = obj.NAME_GOTREF
    426 	p1.To.Type = obj.TYPE_REG
    427 	p1.To.Reg = REGTMP
    428 
    429 	p2.As = p.As
    430 	p2.From = p.From
    431 	p2.To = p.To
    432 	if p.From.Name == obj.NAME_EXTERN {
    433 		p2.From.Reg = REGTMP
    434 		p2.From.Name = obj.NAME_NONE
    435 		p2.From.Sym = nil
    436 	} else if p.To.Name == obj.NAME_EXTERN {
    437 		p2.To.Reg = REGTMP
    438 		p2.To.Name = obj.NAME_NONE
    439 		p2.To.Sym = nil
    440 	} else {
    441 		return
    442 	}
    443 	obj.Nopout(p)
    444 }
    445 
    446 func follow(ctxt *obj.Link, s *obj.LSym) {
    447 	ctxt.Cursym = s
    448 
    449 	firstp := ctxt.NewProg()
    450 	lastp := firstp
    451 	xfol(ctxt, s.Text, &lastp)
    452 	lastp.Link = nil
    453 	s.Text = firstp.Link
    454 }
    455 
    456 func relinv(a obj.As) obj.As {
    457 	switch a {
    458 	case ABEQ:
    459 		return ABNE
    460 	case ABNE:
    461 		return ABEQ
    462 	case ABCS:
    463 		return ABCC
    464 	case ABHS:
    465 		return ABLO
    466 	case ABCC:
    467 		return ABCS
    468 	case ABLO:
    469 		return ABHS
    470 	case ABMI:
    471 		return ABPL
    472 	case ABPL:
    473 		return ABMI
    474 	case ABVS:
    475 		return ABVC
    476 	case ABVC:
    477 		return ABVS
    478 	case ABHI:
    479 		return ABLS
    480 	case ABLS:
    481 		return ABHI
    482 	case ABGE:
    483 		return ABLT
    484 	case ABLT:
    485 		return ABGE
    486 	case ABGT:
    487 		return ABLE
    488 	case ABLE:
    489 		return ABGT
    490 	case ACBZ:
    491 		return ACBNZ
    492 	case ACBNZ:
    493 		return ACBZ
    494 	case ACBZW:
    495 		return ACBNZW
    496 	case ACBNZW:
    497 		return ACBZW
    498 	}
    499 
    500 	log.Fatalf("unknown relation: %s", Anames[a-obj.ABaseARM64])
    501 	return 0
    502 }
    503 
    504 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
    505 	var q *obj.Prog
    506 	var r *obj.Prog
    507 	var i int
    508 
    509 loop:
    510 	if p == nil {
    511 		return
    512 	}
    513 	a := p.As
    514 	if a == AB {
    515 		q = p.Pcond
    516 		if q != nil {
    517 			p.Mark |= FOLL
    518 			p = q
    519 			if !(p.Mark&FOLL != 0) {
    520 				goto loop
    521 			}
    522 		}
    523 	}
    524 
    525 	if p.Mark&FOLL != 0 {
    526 		i = 0
    527 		q = p
    528 		for ; i < 4; i, q = i+1, q.Link {
    529 			if q == *last || q == nil {
    530 				break
    531 			}
    532 			a = q.As
    533 			if a == obj.ANOP {
    534 				i--
    535 				continue
    536 			}
    537 
    538 			if a == AB || a == obj.ARET || a == AERET {
    539 				goto copy
    540 			}
    541 			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
    542 				continue
    543 			}
    544 			if a != ABEQ && a != ABNE {
    545 				continue
    546 			}
    547 
    548 		copy:
    549 			for {
    550 				r = ctxt.NewProg()
    551 				*r = *p
    552 				if !(r.Mark&FOLL != 0) {
    553 					fmt.Printf("can't happen 1\n")
    554 				}
    555 				r.Mark |= FOLL
    556 				if p != q {
    557 					p = p.Link
    558 					(*last).Link = r
    559 					*last = r
    560 					continue
    561 				}
    562 
    563 				(*last).Link = r
    564 				*last = r
    565 				if a == AB || a == obj.ARET || a == AERET {
    566 					return
    567 				}
    568 				if a == ABNE {
    569 					r.As = ABEQ
    570 				} else {
    571 					r.As = ABNE
    572 				}
    573 				r.Pcond = p.Link
    574 				r.Link = p.Pcond
    575 				if !(r.Link.Mark&FOLL != 0) {
    576 					xfol(ctxt, r.Link, last)
    577 				}
    578 				if !(r.Pcond.Mark&FOLL != 0) {
    579 					fmt.Printf("can't happen 2\n")
    580 				}
    581 				return
    582 			}
    583 		}
    584 
    585 		a = AB
    586 		q = ctxt.NewProg()
    587 		q.As = a
    588 		q.Lineno = p.Lineno
    589 		q.To.Type = obj.TYPE_BRANCH
    590 		q.To.Offset = p.Pc
    591 		q.Pcond = p
    592 		p = q
    593 	}
    594 
    595 	p.Mark |= FOLL
    596 	(*last).Link = p
    597 	*last = p
    598 	if a == AB || a == obj.ARET || a == AERET {
    599 		return
    600 	}
    601 	if p.Pcond != nil {
    602 		if a != ABL && p.Link != nil {
    603 			q = obj.Brchain(ctxt, p.Link)
    604 			if a != obj.ATEXT {
    605 				if q != nil && (q.Mark&FOLL != 0) {
    606 					p.As = relinv(a)
    607 					p.Link = p.Pcond
    608 					p.Pcond = q
    609 				}
    610 			}
    611 
    612 			xfol(ctxt, p.Link, last)
    613 			q = obj.Brchain(ctxt, p.Pcond)
    614 			if q == nil {
    615 				q = p.Pcond
    616 			}
    617 			if q.Mark&FOLL != 0 {
    618 				p.Pcond = q
    619 				return
    620 			}
    621 
    622 			p = q
    623 			goto loop
    624 		}
    625 	}
    626 
    627 	p = p.Link
    628 	goto loop
    629 }
    630 
    631 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    632 	ctxt.Cursym = cursym
    633 
    634 	if cursym.Text == nil || cursym.Text.Link == nil {
    635 		return
    636 	}
    637 
    638 	p := cursym.Text
    639 	textstksiz := p.To.Offset
    640 	aoffset := int32(textstksiz)
    641 
    642 	cursym.Args = p.To.Val.(int32)
    643 	cursym.Locals = int32(textstksiz)
    644 
    645 	/*
    646 	 * find leaf subroutines
    647 	 * strip NOPs
    648 	 * expand RET
    649 	 */
    650 	q := (*obj.Prog)(nil)
    651 	var q1 *obj.Prog
    652 	for p := cursym.Text; p != nil; p = p.Link {
    653 		switch p.As {
    654 		case obj.ATEXT:
    655 			p.Mark |= LEAF
    656 
    657 		case obj.ARET:
    658 			break
    659 
    660 		case obj.ANOP:
    661 			q1 = p.Link
    662 			q.Link = q1 /* q is non-nop */
    663 			q1.Mark |= p.Mark
    664 			continue
    665 
    666 		case ABL,
    667 			obj.ADUFFZERO,
    668 			obj.ADUFFCOPY:
    669 			cursym.Text.Mark &^= LEAF
    670 			fallthrough
    671 
    672 		case ACBNZ,
    673 			ACBZ,
    674 			ACBNZW,
    675 			ACBZW,
    676 			ATBZ,
    677 			ATBNZ,
    678 			AB,
    679 			ABEQ,
    680 			ABNE,
    681 			ABCS,
    682 			ABHS,
    683 			ABCC,
    684 			ABLO,
    685 			ABMI,
    686 			ABPL,
    687 			ABVS,
    688 			ABVC,
    689 			ABHI,
    690 			ABLS,
    691 			ABGE,
    692 			ABLT,
    693 			ABGT,
    694 			ABLE,
    695 			AADR, /* strange */
    696 			AADRP:
    697 			q1 = p.Pcond
    698 
    699 			if q1 != nil {
    700 				for q1.As == obj.ANOP {
    701 					q1 = q1.Link
    702 					p.Pcond = q1
    703 				}
    704 			}
    705 
    706 			break
    707 		}
    708 
    709 		q = p
    710 	}
    711 
    712 	var q2 *obj.Prog
    713 	var retjmp *obj.LSym
    714 	for p := cursym.Text; p != nil; p = p.Link {
    715 		o := p.As
    716 		switch o {
    717 		case obj.ATEXT:
    718 			cursym.Text = p
    719 			if textstksiz < 0 {
    720 				ctxt.Autosize = 0
    721 			} else {
    722 				ctxt.Autosize = int32(textstksiz + 8)
    723 			}
    724 			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
    725 				ctxt.Autosize = 0
    726 			} else if ctxt.Autosize&(16-1) != 0 {
    727 				// The frame includes an LR.
    728 				// If the frame size is 8, it's only an LR,
    729 				// so there's no potential for breaking references to
    730 				// local variables by growing the frame size,
    731 				// because there are no local variables.
    732 				// But otherwise, if there is a non-empty locals section,
    733 				// the author of the code is responsible for making sure
    734 				// that the frame size is 8 mod 16.
    735 				if ctxt.Autosize == 8 {
    736 					ctxt.Autosize += 8
    737 					cursym.Locals += 8
    738 				} else {
    739 					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
    740 				}
    741 			}
    742 			p.To.Offset = int64(ctxt.Autosize) - 8
    743 			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
    744 				if ctxt.Debugvlog != 0 {
    745 					ctxt.Logf("save suppressed in: %s\n", cursym.Text.From.Sym.Name)
    746 				}
    747 				cursym.Text.Mark |= LEAF
    748 			}
    749 
    750 			if !(p.From3.Offset&obj.NOSPLIT != 0) {
    751 				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
    752 			}
    753 
    754 			aoffset = ctxt.Autosize
    755 			if aoffset > 0xF0 {
    756 				aoffset = 0xF0
    757 			}
    758 			if cursym.Text.Mark&LEAF != 0 {
    759 				cursym.Set(obj.AttrLeaf, true)
    760 				if ctxt.Autosize == 0 {
    761 					break
    762 				}
    763 			}
    764 
    765 			// Frame is non-empty. Make sure to save link register, even if
    766 			// it is a leaf function, so that traceback works.
    767 			q = p
    768 			if ctxt.Autosize > aoffset {
    769 				// Frame size is too large for a MOVD.W instruction.
    770 				// Store link register before decrementing SP, so if a signal comes
    771 				// during the execution of the function prologue, the traceback
    772 				// code will not see a half-updated stack frame.
    773 				q = obj.Appendp(ctxt, q)
    774 				q.Lineno = p.Lineno
    775 				q.As = ASUB
    776 				q.From.Type = obj.TYPE_CONST
    777 				q.From.Offset = int64(ctxt.Autosize)
    778 				q.Reg = REGSP
    779 				q.To.Type = obj.TYPE_REG
    780 				q.To.Reg = REGTMP
    781 
    782 				q = obj.Appendp(ctxt, q)
    783 				q.Lineno = p.Lineno
    784 				q.As = AMOVD
    785 				q.From.Type = obj.TYPE_REG
    786 				q.From.Reg = REGLINK
    787 				q.To.Type = obj.TYPE_MEM
    788 				q.To.Reg = REGTMP
    789 
    790 				q1 = obj.Appendp(ctxt, q)
    791 				q1.Lineno = p.Lineno
    792 				q1.As = AMOVD
    793 				q1.From.Type = obj.TYPE_REG
    794 				q1.From.Reg = REGTMP
    795 				q1.To.Type = obj.TYPE_REG
    796 				q1.To.Reg = REGSP
    797 				q1.Spadj = ctxt.Autosize
    798 			} else {
    799 				// small frame, update SP and save LR in a single MOVD.W instruction
    800 				q1 = obj.Appendp(ctxt, q)
    801 				q1.As = AMOVD
    802 				q1.Lineno = p.Lineno
    803 				q1.From.Type = obj.TYPE_REG
    804 				q1.From.Reg = REGLINK
    805 				q1.To.Type = obj.TYPE_MEM
    806 				q1.Scond = C_XPRE
    807 				q1.To.Offset = int64(-aoffset)
    808 				q1.To.Reg = REGSP
    809 				q1.Spadj = aoffset
    810 			}
    811 
    812 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    813 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    814 				//
    815 				//	MOV g_panic(g), R1
    816 				//	CMP ZR, R1
    817 				//	BEQ end
    818 				//	MOV panic_argp(R1), R2
    819 				//	ADD $(autosize+8), RSP, R3
    820 				//	CMP R2, R3
    821 				//	BNE end
    822 				//	ADD $8, RSP, R4
    823 				//	MOVD R4, panic_argp(R1)
    824 				// end:
    825 				//	NOP
    826 				//
    827 				// The NOP is needed to give the jumps somewhere to land.
    828 				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
    829 				q = q1
    830 
    831 				q = obj.Appendp(ctxt, q)
    832 				q.As = AMOVD
    833 				q.From.Type = obj.TYPE_MEM
    834 				q.From.Reg = REGG
    835 				q.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
    836 				q.To.Type = obj.TYPE_REG
    837 				q.To.Reg = REG_R1
    838 
    839 				q = obj.Appendp(ctxt, q)
    840 				q.As = ACMP
    841 				q.From.Type = obj.TYPE_REG
    842 				q.From.Reg = REGZERO
    843 				q.Reg = REG_R1
    844 
    845 				q = obj.Appendp(ctxt, q)
    846 				q.As = ABEQ
    847 				q.To.Type = obj.TYPE_BRANCH
    848 				q1 = q
    849 
    850 				q = obj.Appendp(ctxt, q)
    851 				q.As = AMOVD
    852 				q.From.Type = obj.TYPE_MEM
    853 				q.From.Reg = REG_R1
    854 				q.From.Offset = 0 // Panic.argp
    855 				q.To.Type = obj.TYPE_REG
    856 				q.To.Reg = REG_R2
    857 
    858 				q = obj.Appendp(ctxt, q)
    859 				q.As = AADD
    860 				q.From.Type = obj.TYPE_CONST
    861 				q.From.Offset = int64(ctxt.Autosize) + 8
    862 				q.Reg = REGSP
    863 				q.To.Type = obj.TYPE_REG
    864 				q.To.Reg = REG_R3
    865 
    866 				q = obj.Appendp(ctxt, q)
    867 				q.As = ACMP
    868 				q.From.Type = obj.TYPE_REG
    869 				q.From.Reg = REG_R2
    870 				q.Reg = REG_R3
    871 
    872 				q = obj.Appendp(ctxt, q)
    873 				q.As = ABNE
    874 				q.To.Type = obj.TYPE_BRANCH
    875 				q2 = q
    876 
    877 				q = obj.Appendp(ctxt, q)
    878 				q.As = AADD
    879 				q.From.Type = obj.TYPE_CONST
    880 				q.From.Offset = 8
    881 				q.Reg = REGSP
    882 				q.To.Type = obj.TYPE_REG
    883 				q.To.Reg = REG_R4
    884 
    885 				q = obj.Appendp(ctxt, q)
    886 				q.As = AMOVD
    887 				q.From.Type = obj.TYPE_REG
    888 				q.From.Reg = REG_R4
    889 				q.To.Type = obj.TYPE_MEM
    890 				q.To.Reg = REG_R1
    891 				q.To.Offset = 0 // Panic.argp
    892 
    893 				q = obj.Appendp(ctxt, q)
    894 
    895 				q.As = obj.ANOP
    896 				q1.Pcond = q
    897 				q2.Pcond = q
    898 			}
    899 
    900 		case obj.ARET:
    901 			nocache(p)
    902 			if p.From.Type == obj.TYPE_CONST {
    903 				ctxt.Diag("using BECOME (%v) is not supported!", p)
    904 				break
    905 			}
    906 
    907 			retjmp = p.To.Sym
    908 			p.To = obj.Addr{}
    909 			if cursym.Text.Mark&LEAF != 0 {
    910 				if ctxt.Autosize != 0 {
    911 					p.As = AADD
    912 					p.From.Type = obj.TYPE_CONST
    913 					p.From.Offset = int64(ctxt.Autosize)
    914 					p.To.Type = obj.TYPE_REG
    915 					p.To.Reg = REGSP
    916 					p.Spadj = -ctxt.Autosize
    917 				}
    918 			} else {
    919 				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
    920 				aoffset = ctxt.Autosize
    921 
    922 				if aoffset > 0xF0 {
    923 					aoffset = 0xF0
    924 				}
    925 				p.As = AMOVD
    926 				p.From.Type = obj.TYPE_MEM
    927 				p.Scond = C_XPOST
    928 				p.From.Offset = int64(aoffset)
    929 				p.From.Reg = REGSP
    930 				p.To.Type = obj.TYPE_REG
    931 				p.To.Reg = REGLINK
    932 				p.Spadj = -aoffset
    933 				if ctxt.Autosize > aoffset {
    934 					q = ctxt.NewProg()
    935 					q.As = AADD
    936 					q.From.Type = obj.TYPE_CONST
    937 					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
    938 					q.To.Type = obj.TYPE_REG
    939 					q.To.Reg = REGSP
    940 					q.Link = p.Link
    941 					q.Spadj = int32(-q.From.Offset)
    942 					q.Lineno = p.Lineno
    943 					p.Link = q
    944 					p = q
    945 				}
    946 			}
    947 
    948 			if p.As != obj.ARET {
    949 				q = ctxt.NewProg()
    950 				q.Lineno = p.Lineno
    951 				q.Link = p.Link
    952 				p.Link = q
    953 				p = q
    954 			}
    955 
    956 			if retjmp != nil { // retjmp
    957 				p.As = AB
    958 				p.To.Type = obj.TYPE_BRANCH
    959 				p.To.Sym = retjmp
    960 				p.Spadj = +ctxt.Autosize
    961 				break
    962 			}
    963 
    964 			p.As = obj.ARET
    965 			p.To.Type = obj.TYPE_MEM
    966 			p.To.Offset = 0
    967 			p.To.Reg = REGLINK
    968 			p.Spadj = +ctxt.Autosize
    969 
    970 		case AADD, ASUB:
    971 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    972 				if p.As == AADD {
    973 					p.Spadj = int32(-p.From.Offset)
    974 				} else {
    975 					p.Spadj = int32(+p.From.Offset)
    976 				}
    977 			}
    978 			break
    979 		}
    980 	}
    981 }
    982 
    983 func nocache(p *obj.Prog) {
    984 	p.Optab = 0
    985 	p.From.Class = 0
    986 	p.To.Class = 0
    987 }
    988 
    989 var unaryDst = map[obj.As]bool{
    990 	AWORD:  true,
    991 	ADWORD: true,
    992 	ABL:    true,
    993 	AB:     true,
    994 	ASVC:   true,
    995 }
    996 
    997 var Linkarm64 = obj.LinkArch{
    998 	Arch:       sys.ArchARM64,
    999 	Preprocess: preprocess,
   1000 	Assemble:   span7,
   1001 	Follow:     follow,
   1002 	Progedit:   progedit,
   1003 	UnaryDst:   unaryDst,
   1004 }
   1005