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 	"encoding/binary"
     36 	"fmt"
     37 	"log"
     38 	"math"
     39 )
     40 
     41 var complements = []int16{
     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 != 0 {
     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 	// MOV	LR, R3
    165 	movlr := obj.Appendp(ctxt, last)
    166 	movlr.As = AMOVD
    167 	movlr.From.Type = obj.TYPE_REG
    168 	movlr.From.Reg = REGLINK
    169 	movlr.To.Type = obj.TYPE_REG
    170 	movlr.To.Reg = REG_R3
    171 	if q != nil {
    172 		q.Pcond = movlr
    173 	}
    174 	bls.Pcond = movlr
    175 
    176 	debug := movlr
    177 	if false {
    178 		debug = obj.Appendp(ctxt, debug)
    179 		debug.As = AMOVD
    180 		debug.From.Type = obj.TYPE_CONST
    181 		debug.From.Offset = int64(framesize)
    182 		debug.To.Type = obj.TYPE_REG
    183 		debug.To.Reg = REGTMP
    184 	}
    185 
    186 	// BL	runtime.morestack(SB)
    187 	call := obj.Appendp(ctxt, debug)
    188 	call.As = ABL
    189 	call.To.Type = obj.TYPE_BRANCH
    190 	morestack := "runtime.morestack"
    191 	switch {
    192 	case ctxt.Cursym.Cfunc != 0:
    193 		morestack = "runtime.morestackc"
    194 	case ctxt.Cursym.Text.From3.Offset&obj.NEEDCTXT == 0:
    195 		morestack = "runtime.morestack_noctxt"
    196 	}
    197 	call.To.Sym = obj.Linklookup(ctxt, morestack, 0)
    198 
    199 	// B	start
    200 	jmp := obj.Appendp(ctxt, call)
    201 	jmp.As = AB
    202 	jmp.To.Type = obj.TYPE_BRANCH
    203 	jmp.Pcond = ctxt.Cursym.Text.Link
    204 
    205 	// placeholder for bls's jump target
    206 	// p = obj.Appendp(ctxt, p)
    207 	// p.As = obj.ANOP
    208 
    209 	return bls
    210 }
    211 
    212 func progedit(ctxt *obj.Link, p *obj.Prog) {
    213 	p.From.Class = 0
    214 	p.To.Class = 0
    215 
    216 	// $0 results in C_ZCON, which matches both C_REG and various
    217 	// C_xCON, however the C_REG cases in asmout don't expect a
    218 	// constant, so they will use the register fields and assemble
    219 	// a R0. To prevent that, rewrite $0 as ZR.
    220 	if p.From.Type == obj.TYPE_CONST && p.From.Offset == 0 {
    221 		p.From.Type = obj.TYPE_REG
    222 		p.From.Reg = REGZERO
    223 	}
    224 	if p.To.Type == obj.TYPE_CONST && p.To.Offset == 0 {
    225 		p.To.Type = obj.TYPE_REG
    226 		p.To.Reg = REGZERO
    227 	}
    228 
    229 	// Rewrite BR/BL to symbol as TYPE_BRANCH.
    230 	switch p.As {
    231 	case AB,
    232 		ABL,
    233 		obj.ARET,
    234 		obj.ADUFFZERO,
    235 		obj.ADUFFCOPY:
    236 		if p.To.Sym != nil {
    237 			p.To.Type = obj.TYPE_BRANCH
    238 		}
    239 		break
    240 	}
    241 
    242 	// Rewrite float constants to values stored in memory.
    243 	switch p.As {
    244 	case AFMOVS:
    245 		if p.From.Type == obj.TYPE_FCONST {
    246 			f32 := float32(p.From.Val.(float64))
    247 			i32 := math.Float32bits(f32)
    248 			literal := fmt.Sprintf("$f32.%08x", uint32(i32))
    249 			s := obj.Linklookup(ctxt, literal, 0)
    250 			s.Size = 4
    251 			p.From.Type = obj.TYPE_MEM
    252 			p.From.Sym = s
    253 			p.From.Name = obj.NAME_EXTERN
    254 			p.From.Offset = 0
    255 		}
    256 
    257 	case AFMOVD:
    258 		if p.From.Type == obj.TYPE_FCONST {
    259 			i64 := math.Float64bits(p.From.Val.(float64))
    260 			literal := fmt.Sprintf("$f64.%016x", uint64(i64))
    261 			s := obj.Linklookup(ctxt, literal, 0)
    262 			s.Size = 8
    263 			p.From.Type = obj.TYPE_MEM
    264 			p.From.Sym = s
    265 			p.From.Name = obj.NAME_EXTERN
    266 			p.From.Offset = 0
    267 		}
    268 
    269 		break
    270 	}
    271 
    272 	// Rewrite negative immediates as positive immediates with
    273 	// complementary instruction.
    274 	switch p.As {
    275 	case AADD,
    276 		AADDW,
    277 		ASUB,
    278 		ASUBW,
    279 		ACMP,
    280 		ACMPW,
    281 		ACMN,
    282 		ACMNW:
    283 		if p.From.Type == obj.NAME_EXTERN && p.From.Offset < 0 {
    284 			p.From.Offset = -p.From.Offset
    285 			p.As = complements[p.As]
    286 		}
    287 
    288 		break
    289 	}
    290 }
    291 
    292 func follow(ctxt *obj.Link, s *obj.LSym) {
    293 	ctxt.Cursym = s
    294 
    295 	firstp := ctxt.NewProg()
    296 	lastp := firstp
    297 	xfol(ctxt, s.Text, &lastp)
    298 	lastp.Link = nil
    299 	s.Text = firstp.Link
    300 }
    301 
    302 func relinv(a int) int {
    303 	switch a {
    304 	case ABEQ:
    305 		return ABNE
    306 	case ABNE:
    307 		return ABEQ
    308 	case ABCS:
    309 		return ABCC
    310 	case ABHS:
    311 		return ABLO
    312 	case ABCC:
    313 		return ABCS
    314 	case ABLO:
    315 		return ABHS
    316 	case ABMI:
    317 		return ABPL
    318 	case ABPL:
    319 		return ABMI
    320 	case ABVS:
    321 		return ABVC
    322 	case ABVC:
    323 		return ABVS
    324 	case ABHI:
    325 		return ABLS
    326 	case ABLS:
    327 		return ABHI
    328 	case ABGE:
    329 		return ABLT
    330 	case ABLT:
    331 		return ABGE
    332 	case ABGT:
    333 		return ABLE
    334 	case ABLE:
    335 		return ABGT
    336 	}
    337 
    338 	log.Fatalf("unknown relation: %s", Anames[a])
    339 	return 0
    340 }
    341 
    342 func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
    343 	var q *obj.Prog
    344 	var r *obj.Prog
    345 	var a int
    346 	var i int
    347 
    348 loop:
    349 	if p == nil {
    350 		return
    351 	}
    352 	a = int(p.As)
    353 	if a == AB {
    354 		q = p.Pcond
    355 		if q != nil {
    356 			p.Mark |= FOLL
    357 			p = q
    358 			if !(p.Mark&FOLL != 0) {
    359 				goto loop
    360 			}
    361 		}
    362 	}
    363 
    364 	if p.Mark&FOLL != 0 {
    365 		i = 0
    366 		q = p
    367 		for ; i < 4; i, q = i+1, q.Link {
    368 			if q == *last || q == nil {
    369 				break
    370 			}
    371 			a = int(q.As)
    372 			if a == obj.ANOP {
    373 				i--
    374 				continue
    375 			}
    376 
    377 			if a == AB || a == obj.ARET || a == AERET {
    378 				goto copy
    379 			}
    380 			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
    381 				continue
    382 			}
    383 			if a != ABEQ && a != ABNE {
    384 				continue
    385 			}
    386 
    387 		copy:
    388 			for {
    389 				r = ctxt.NewProg()
    390 				*r = *p
    391 				if !(r.Mark&FOLL != 0) {
    392 					fmt.Printf("cant happen 1\n")
    393 				}
    394 				r.Mark |= FOLL
    395 				if p != q {
    396 					p = p.Link
    397 					(*last).Link = r
    398 					*last = r
    399 					continue
    400 				}
    401 
    402 				(*last).Link = r
    403 				*last = r
    404 				if a == AB || a == obj.ARET || a == AERET {
    405 					return
    406 				}
    407 				if a == ABNE {
    408 					r.As = ABEQ
    409 				} else {
    410 					r.As = ABNE
    411 				}
    412 				r.Pcond = p.Link
    413 				r.Link = p.Pcond
    414 				if !(r.Link.Mark&FOLL != 0) {
    415 					xfol(ctxt, r.Link, last)
    416 				}
    417 				if !(r.Pcond.Mark&FOLL != 0) {
    418 					fmt.Printf("cant happen 2\n")
    419 				}
    420 				return
    421 			}
    422 		}
    423 
    424 		a = AB
    425 		q = ctxt.NewProg()
    426 		q.As = int16(a)
    427 		q.Lineno = p.Lineno
    428 		q.To.Type = obj.TYPE_BRANCH
    429 		q.To.Offset = p.Pc
    430 		q.Pcond = p
    431 		p = q
    432 	}
    433 
    434 	p.Mark |= FOLL
    435 	(*last).Link = p
    436 	*last = p
    437 	if a == AB || a == obj.ARET || a == AERET {
    438 		return
    439 	}
    440 	if p.Pcond != nil {
    441 		if a != ABL && p.Link != nil {
    442 			q = obj.Brchain(ctxt, p.Link)
    443 			if a != obj.ATEXT && a != ABCASE {
    444 				if q != nil && (q.Mark&FOLL != 0) {
    445 					p.As = int16(relinv(a))
    446 					p.Link = p.Pcond
    447 					p.Pcond = q
    448 				}
    449 			}
    450 
    451 			xfol(ctxt, p.Link, last)
    452 			q = obj.Brchain(ctxt, p.Pcond)
    453 			if q == nil {
    454 				q = p.Pcond
    455 			}
    456 			if q.Mark&FOLL != 0 {
    457 				p.Pcond = q
    458 				return
    459 			}
    460 
    461 			p = q
    462 			goto loop
    463 		}
    464 	}
    465 
    466 	p = p.Link
    467 	goto loop
    468 }
    469 
    470 func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
    471 	ctxt.Cursym = cursym
    472 
    473 	if cursym.Text == nil || cursym.Text.Link == nil {
    474 		return
    475 	}
    476 
    477 	p := cursym.Text
    478 	textstksiz := p.To.Offset
    479 	aoffset := int32(textstksiz)
    480 
    481 	cursym.Args = p.To.Val.(int32)
    482 	cursym.Locals = int32(textstksiz)
    483 
    484 	/*
    485 	 * find leaf subroutines
    486 	 * strip NOPs
    487 	 * expand RET
    488 	 */
    489 	ctxt.Bso.Flush()
    490 	q := (*obj.Prog)(nil)
    491 	var q1 *obj.Prog
    492 	for p := cursym.Text; p != nil; p = p.Link {
    493 		switch p.As {
    494 		case obj.ATEXT:
    495 			p.Mark |= LEAF
    496 
    497 		case obj.ARET:
    498 			break
    499 
    500 		case obj.ANOP:
    501 			q1 = p.Link
    502 			q.Link = q1 /* q is non-nop */
    503 			q1.Mark |= p.Mark
    504 			continue
    505 
    506 		case ABL,
    507 			obj.ADUFFZERO,
    508 			obj.ADUFFCOPY:
    509 			cursym.Text.Mark &^= LEAF
    510 			fallthrough
    511 
    512 		case ACBNZ,
    513 			ACBZ,
    514 			ACBNZW,
    515 			ACBZW,
    516 			ATBZ,
    517 			ATBNZ,
    518 			ABCASE,
    519 			AB,
    520 			ABEQ,
    521 			ABNE,
    522 			ABCS,
    523 			ABHS,
    524 			ABCC,
    525 			ABLO,
    526 			ABMI,
    527 			ABPL,
    528 			ABVS,
    529 			ABVC,
    530 			ABHI,
    531 			ABLS,
    532 			ABGE,
    533 			ABLT,
    534 			ABGT,
    535 			ABLE,
    536 			AADR, /* strange */
    537 			AADRP:
    538 			q1 = p.Pcond
    539 
    540 			if q1 != nil {
    541 				for q1.As == obj.ANOP {
    542 					q1 = q1.Link
    543 					p.Pcond = q1
    544 				}
    545 			}
    546 
    547 			break
    548 		}
    549 
    550 		q = p
    551 	}
    552 
    553 	var o int
    554 	var q2 *obj.Prog
    555 	var retjmp *obj.LSym
    556 	for p := cursym.Text; p != nil; p = p.Link {
    557 		o = int(p.As)
    558 		switch o {
    559 		case obj.ATEXT:
    560 			cursym.Text = p
    561 			if textstksiz < 0 {
    562 				ctxt.Autosize = 0
    563 			} else {
    564 				ctxt.Autosize = int32(textstksiz + 8)
    565 			}
    566 			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
    567 				ctxt.Autosize = 0
    568 			} else if ctxt.Autosize&(16-1) != 0 {
    569 				// The frame includes an LR.
    570 				// If the frame size is 8, it's only an LR,
    571 				// so there's no potential for breaking references to
    572 				// local variables by growing the frame size,
    573 				// because there are no local variables.
    574 				// But otherwise, if there is a non-empty locals section,
    575 				// the author of the code is responsible for making sure
    576 				// that the frame size is 8 mod 16.
    577 				if ctxt.Autosize == 8 {
    578 					ctxt.Autosize += 8
    579 					cursym.Locals += 8
    580 				} else {
    581 					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
    582 				}
    583 			}
    584 			p.To.Offset = int64(ctxt.Autosize) - 8
    585 			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
    586 				if ctxt.Debugvlog != 0 {
    587 					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name)
    588 				}
    589 				ctxt.Bso.Flush()
    590 				cursym.Text.Mark |= LEAF
    591 			}
    592 
    593 			if !(p.From3.Offset&obj.NOSPLIT != 0) {
    594 				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
    595 			}
    596 
    597 			aoffset = ctxt.Autosize
    598 			if aoffset > 0xF0 {
    599 				aoffset = 0xF0
    600 			}
    601 			if cursym.Text.Mark&LEAF != 0 {
    602 				cursym.Leaf = 1
    603 				if ctxt.Autosize == 0 {
    604 					break
    605 				}
    606 				aoffset = 0
    607 			}
    608 
    609 			q = p
    610 			if ctxt.Autosize > aoffset {
    611 				q = ctxt.NewProg()
    612 				q.As = ASUB
    613 				q.Lineno = p.Lineno
    614 				q.From.Type = obj.TYPE_CONST
    615 				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
    616 				q.To.Type = obj.TYPE_REG
    617 				q.To.Reg = REGSP
    618 				q.Spadj = int32(q.From.Offset)
    619 				q.Link = p.Link
    620 				p.Link = q
    621 				if cursym.Text.Mark&LEAF != 0 {
    622 					break
    623 				}
    624 			}
    625 
    626 			q1 = ctxt.NewProg()
    627 			q1.As = AMOVD
    628 			q1.Lineno = p.Lineno
    629 			q1.From.Type = obj.TYPE_REG
    630 			q1.From.Reg = REGLINK
    631 			q1.To.Type = obj.TYPE_MEM
    632 			q1.Scond = C_XPRE
    633 			q1.To.Offset = int64(-aoffset)
    634 			q1.To.Reg = REGSP
    635 			q1.Link = q.Link
    636 			q1.Spadj = aoffset
    637 			q.Link = q1
    638 
    639 			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
    640 				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
    641 				//
    642 				//	MOV g_panic(g), R1
    643 				//	CMP ZR, R1
    644 				//	BEQ end
    645 				//	MOV panic_argp(R1), R2
    646 				//	ADD $(autosize+8), RSP, R3
    647 				//	CMP R2, R3
    648 				//	BNE end
    649 				//	ADD $8, RSP, R4
    650 				//	MOVD R4, panic_argp(R1)
    651 				// end:
    652 				//	NOP
    653 				//
    654 				// The NOP is needed to give the jumps somewhere to land.
    655 				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
    656 				q = q1
    657 
    658 				q = obj.Appendp(ctxt, q)
    659 				q.As = AMOVD
    660 				q.From.Type = obj.TYPE_MEM
    661 				q.From.Reg = REGG
    662 				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
    663 				q.To.Type = obj.TYPE_REG
    664 				q.To.Reg = REG_R1
    665 
    666 				q = obj.Appendp(ctxt, q)
    667 				q.As = ACMP
    668 				q.From.Type = obj.TYPE_REG
    669 				q.From.Reg = REGZERO
    670 				q.Reg = REG_R1
    671 
    672 				q = obj.Appendp(ctxt, q)
    673 				q.As = ABEQ
    674 				q.To.Type = obj.TYPE_BRANCH
    675 				q1 = q
    676 
    677 				q = obj.Appendp(ctxt, q)
    678 				q.As = AMOVD
    679 				q.From.Type = obj.TYPE_MEM
    680 				q.From.Reg = REG_R1
    681 				q.From.Offset = 0 // Panic.argp
    682 				q.To.Type = obj.TYPE_REG
    683 				q.To.Reg = REG_R2
    684 
    685 				q = obj.Appendp(ctxt, q)
    686 				q.As = AADD
    687 				q.From.Type = obj.TYPE_CONST
    688 				q.From.Offset = int64(ctxt.Autosize) + 8
    689 				q.Reg = REGSP
    690 				q.To.Type = obj.TYPE_REG
    691 				q.To.Reg = REG_R3
    692 
    693 				q = obj.Appendp(ctxt, q)
    694 				q.As = ACMP
    695 				q.From.Type = obj.TYPE_REG
    696 				q.From.Reg = REG_R2
    697 				q.Reg = REG_R3
    698 
    699 				q = obj.Appendp(ctxt, q)
    700 				q.As = ABNE
    701 				q.To.Type = obj.TYPE_BRANCH
    702 				q2 = q
    703 
    704 				q = obj.Appendp(ctxt, q)
    705 				q.As = AADD
    706 				q.From.Type = obj.TYPE_CONST
    707 				q.From.Offset = 8
    708 				q.Reg = REGSP
    709 				q.To.Type = obj.TYPE_REG
    710 				q.To.Reg = REG_R4
    711 
    712 				q = obj.Appendp(ctxt, q)
    713 				q.As = AMOVD
    714 				q.From.Type = obj.TYPE_REG
    715 				q.From.Reg = REG_R4
    716 				q.To.Type = obj.TYPE_MEM
    717 				q.To.Reg = REG_R1
    718 				q.To.Offset = 0 // Panic.argp
    719 
    720 				q = obj.Appendp(ctxt, q)
    721 
    722 				q.As = obj.ANOP
    723 				q1.Pcond = q
    724 				q2.Pcond = q
    725 			}
    726 
    727 		case obj.ARET:
    728 			nocache(p)
    729 			if p.From.Type == obj.TYPE_CONST {
    730 				ctxt.Diag("using BECOME (%v) is not supported!", p)
    731 				break
    732 			}
    733 
    734 			retjmp = p.To.Sym
    735 			p.To = obj.Addr{}
    736 			if cursym.Text.Mark&LEAF != 0 {
    737 				if ctxt.Autosize != 0 {
    738 					p.As = AADD
    739 					p.From.Type = obj.TYPE_CONST
    740 					p.From.Offset = int64(ctxt.Autosize)
    741 					p.To.Type = obj.TYPE_REG
    742 					p.To.Reg = REGSP
    743 					p.Spadj = -ctxt.Autosize
    744 				}
    745 			} else {
    746 				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
    747 				aoffset = ctxt.Autosize
    748 
    749 				if aoffset > 0xF0 {
    750 					aoffset = 0xF0
    751 				}
    752 				p.As = AMOVD
    753 				p.From.Type = obj.TYPE_MEM
    754 				p.Scond = C_XPOST
    755 				p.From.Offset = int64(aoffset)
    756 				p.From.Reg = REGSP
    757 				p.To.Type = obj.TYPE_REG
    758 				p.To.Reg = REGLINK
    759 				p.Spadj = -aoffset
    760 				if ctxt.Autosize > aoffset {
    761 					q = ctxt.NewProg()
    762 					q.As = AADD
    763 					q.From.Type = obj.TYPE_CONST
    764 					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
    765 					q.To.Type = obj.TYPE_REG
    766 					q.To.Reg = REGSP
    767 					q.Link = p.Link
    768 					q.Spadj = int32(-q.From.Offset)
    769 					q.Lineno = p.Lineno
    770 					p.Link = q
    771 					p = q
    772 				}
    773 			}
    774 
    775 			if p.As != obj.ARET {
    776 				q = ctxt.NewProg()
    777 				q.Lineno = p.Lineno
    778 				q.Link = p.Link
    779 				p.Link = q
    780 				p = q
    781 			}
    782 
    783 			if retjmp != nil { // retjmp
    784 				p.As = AB
    785 				p.To.Type = obj.TYPE_BRANCH
    786 				p.To.Sym = retjmp
    787 				p.Spadj = +ctxt.Autosize
    788 				break
    789 			}
    790 
    791 			p.As = obj.ARET
    792 			p.To.Type = obj.TYPE_MEM
    793 			p.To.Offset = 0
    794 			p.To.Reg = REGLINK
    795 			p.Spadj = +ctxt.Autosize
    796 
    797 		case AADD, ASUB:
    798 			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
    799 				if p.As == AADD {
    800 					p.Spadj = int32(-p.From.Offset)
    801 				} else {
    802 					p.Spadj = int32(+p.From.Offset)
    803 				}
    804 			}
    805 			break
    806 		}
    807 	}
    808 }
    809 
    810 func nocache(p *obj.Prog) {
    811 	p.Optab = 0
    812 	p.From.Class = 0
    813 	p.To.Class = 0
    814 }
    815 
    816 var unaryDst = map[int]bool{
    817 	AWORD:  true,
    818 	ADWORD: true,
    819 	ABL:    true,
    820 	AB:     true,
    821 	ASVC:   true,
    822 }
    823 
    824 var Linkarm64 = obj.LinkArch{
    825 	ByteOrder:  binary.LittleEndian,
    826 	Name:       "arm64",
    827 	Thechar:    '7',
    828 	Preprocess: preprocess,
    829 	Assemble:   span7,
    830 	Follow:     follow,
    831 	Progedit:   progedit,
    832 	UnaryDst:   unaryDst,
    833 	Minlc:      4,
    834 	Ptrsize:    8,
    835 	Regsize:    8,
    836 }
    837