github.com/q45/go@v0.0.0-20151101211701-a4fb8c13db3f/src/cmd/internal/obj/arm64/obj7.go (about)

     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  	"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 {
   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  			AB,
   519  			ABEQ,
   520  			ABNE,
   521  			ABCS,
   522  			ABHS,
   523  			ABCC,
   524  			ABLO,
   525  			ABMI,
   526  			ABPL,
   527  			ABVS,
   528  			ABVC,
   529  			ABHI,
   530  			ABLS,
   531  			ABGE,
   532  			ABLT,
   533  			ABGT,
   534  			ABLE,
   535  			AADR, /* strange */
   536  			AADRP:
   537  			q1 = p.Pcond
   538  
   539  			if q1 != nil {
   540  				for q1.As == obj.ANOP {
   541  					q1 = q1.Link
   542  					p.Pcond = q1
   543  				}
   544  			}
   545  
   546  			break
   547  		}
   548  
   549  		q = p
   550  	}
   551  
   552  	var o int
   553  	var q2 *obj.Prog
   554  	var retjmp *obj.LSym
   555  	for p := cursym.Text; p != nil; p = p.Link {
   556  		o = int(p.As)
   557  		switch o {
   558  		case obj.ATEXT:
   559  			cursym.Text = p
   560  			if textstksiz < 0 {
   561  				ctxt.Autosize = 0
   562  			} else {
   563  				ctxt.Autosize = int32(textstksiz + 8)
   564  			}
   565  			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
   566  				ctxt.Autosize = 0
   567  			} else if ctxt.Autosize&(16-1) != 0 {
   568  				// The frame includes an LR.
   569  				// If the frame size is 8, it's only an LR,
   570  				// so there's no potential for breaking references to
   571  				// local variables by growing the frame size,
   572  				// because there are no local variables.
   573  				// But otherwise, if there is a non-empty locals section,
   574  				// the author of the code is responsible for making sure
   575  				// that the frame size is 8 mod 16.
   576  				if ctxt.Autosize == 8 {
   577  					ctxt.Autosize += 8
   578  					cursym.Locals += 8
   579  				} else {
   580  					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
   581  				}
   582  			}
   583  			p.To.Offset = int64(ctxt.Autosize) - 8
   584  			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
   585  				if ctxt.Debugvlog != 0 {
   586  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name)
   587  				}
   588  				ctxt.Bso.Flush()
   589  				cursym.Text.Mark |= LEAF
   590  			}
   591  
   592  			if !(p.From3.Offset&obj.NOSPLIT != 0) {
   593  				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
   594  			}
   595  
   596  			aoffset = ctxt.Autosize
   597  			if aoffset > 0xF0 {
   598  				aoffset = 0xF0
   599  			}
   600  			if cursym.Text.Mark&LEAF != 0 {
   601  				cursym.Leaf = 1
   602  				if ctxt.Autosize == 0 {
   603  					break
   604  				}
   605  				aoffset = 0
   606  			}
   607  
   608  			q = p
   609  			if ctxt.Autosize > aoffset {
   610  				q = ctxt.NewProg()
   611  				q.As = ASUB
   612  				q.Lineno = p.Lineno
   613  				q.From.Type = obj.TYPE_CONST
   614  				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   615  				q.To.Type = obj.TYPE_REG
   616  				q.To.Reg = REGSP
   617  				q.Spadj = int32(q.From.Offset)
   618  				q.Link = p.Link
   619  				p.Link = q
   620  				if cursym.Text.Mark&LEAF != 0 {
   621  					break
   622  				}
   623  			}
   624  
   625  			q1 = ctxt.NewProg()
   626  			q1.As = AMOVD
   627  			q1.Lineno = p.Lineno
   628  			q1.From.Type = obj.TYPE_REG
   629  			q1.From.Reg = REGLINK
   630  			q1.To.Type = obj.TYPE_MEM
   631  			q1.Scond = C_XPRE
   632  			q1.To.Offset = int64(-aoffset)
   633  			q1.To.Reg = REGSP
   634  			q1.Link = q.Link
   635  			q1.Spadj = aoffset
   636  			q.Link = q1
   637  
   638  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   639  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   640  				//
   641  				//	MOV g_panic(g), R1
   642  				//	CMP ZR, R1
   643  				//	BEQ end
   644  				//	MOV panic_argp(R1), R2
   645  				//	ADD $(autosize+8), RSP, R3
   646  				//	CMP R2, R3
   647  				//	BNE end
   648  				//	ADD $8, RSP, R4
   649  				//	MOVD R4, panic_argp(R1)
   650  				// end:
   651  				//	NOP
   652  				//
   653  				// The NOP is needed to give the jumps somewhere to land.
   654  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   655  				q = q1
   656  
   657  				q = obj.Appendp(ctxt, q)
   658  				q.As = AMOVD
   659  				q.From.Type = obj.TYPE_MEM
   660  				q.From.Reg = REGG
   661  				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   662  				q.To.Type = obj.TYPE_REG
   663  				q.To.Reg = REG_R1
   664  
   665  				q = obj.Appendp(ctxt, q)
   666  				q.As = ACMP
   667  				q.From.Type = obj.TYPE_REG
   668  				q.From.Reg = REGZERO
   669  				q.Reg = REG_R1
   670  
   671  				q = obj.Appendp(ctxt, q)
   672  				q.As = ABEQ
   673  				q.To.Type = obj.TYPE_BRANCH
   674  				q1 = q
   675  
   676  				q = obj.Appendp(ctxt, q)
   677  				q.As = AMOVD
   678  				q.From.Type = obj.TYPE_MEM
   679  				q.From.Reg = REG_R1
   680  				q.From.Offset = 0 // Panic.argp
   681  				q.To.Type = obj.TYPE_REG
   682  				q.To.Reg = REG_R2
   683  
   684  				q = obj.Appendp(ctxt, q)
   685  				q.As = AADD
   686  				q.From.Type = obj.TYPE_CONST
   687  				q.From.Offset = int64(ctxt.Autosize) + 8
   688  				q.Reg = REGSP
   689  				q.To.Type = obj.TYPE_REG
   690  				q.To.Reg = REG_R3
   691  
   692  				q = obj.Appendp(ctxt, q)
   693  				q.As = ACMP
   694  				q.From.Type = obj.TYPE_REG
   695  				q.From.Reg = REG_R2
   696  				q.Reg = REG_R3
   697  
   698  				q = obj.Appendp(ctxt, q)
   699  				q.As = ABNE
   700  				q.To.Type = obj.TYPE_BRANCH
   701  				q2 = q
   702  
   703  				q = obj.Appendp(ctxt, q)
   704  				q.As = AADD
   705  				q.From.Type = obj.TYPE_CONST
   706  				q.From.Offset = 8
   707  				q.Reg = REGSP
   708  				q.To.Type = obj.TYPE_REG
   709  				q.To.Reg = REG_R4
   710  
   711  				q = obj.Appendp(ctxt, q)
   712  				q.As = AMOVD
   713  				q.From.Type = obj.TYPE_REG
   714  				q.From.Reg = REG_R4
   715  				q.To.Type = obj.TYPE_MEM
   716  				q.To.Reg = REG_R1
   717  				q.To.Offset = 0 // Panic.argp
   718  
   719  				q = obj.Appendp(ctxt, q)
   720  
   721  				q.As = obj.ANOP
   722  				q1.Pcond = q
   723  				q2.Pcond = q
   724  			}
   725  
   726  		case obj.ARET:
   727  			nocache(p)
   728  			if p.From.Type == obj.TYPE_CONST {
   729  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   730  				break
   731  			}
   732  
   733  			retjmp = p.To.Sym
   734  			p.To = obj.Addr{}
   735  			if cursym.Text.Mark&LEAF != 0 {
   736  				if ctxt.Autosize != 0 {
   737  					p.As = AADD
   738  					p.From.Type = obj.TYPE_CONST
   739  					p.From.Offset = int64(ctxt.Autosize)
   740  					p.To.Type = obj.TYPE_REG
   741  					p.To.Reg = REGSP
   742  					p.Spadj = -ctxt.Autosize
   743  				}
   744  			} else {
   745  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   746  				aoffset = ctxt.Autosize
   747  
   748  				if aoffset > 0xF0 {
   749  					aoffset = 0xF0
   750  				}
   751  				p.As = AMOVD
   752  				p.From.Type = obj.TYPE_MEM
   753  				p.Scond = C_XPOST
   754  				p.From.Offset = int64(aoffset)
   755  				p.From.Reg = REGSP
   756  				p.To.Type = obj.TYPE_REG
   757  				p.To.Reg = REGLINK
   758  				p.Spadj = -aoffset
   759  				if ctxt.Autosize > aoffset {
   760  					q = ctxt.NewProg()
   761  					q.As = AADD
   762  					q.From.Type = obj.TYPE_CONST
   763  					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   764  					q.To.Type = obj.TYPE_REG
   765  					q.To.Reg = REGSP
   766  					q.Link = p.Link
   767  					q.Spadj = int32(-q.From.Offset)
   768  					q.Lineno = p.Lineno
   769  					p.Link = q
   770  					p = q
   771  				}
   772  			}
   773  
   774  			if p.As != obj.ARET {
   775  				q = ctxt.NewProg()
   776  				q.Lineno = p.Lineno
   777  				q.Link = p.Link
   778  				p.Link = q
   779  				p = q
   780  			}
   781  
   782  			if retjmp != nil { // retjmp
   783  				p.As = AB
   784  				p.To.Type = obj.TYPE_BRANCH
   785  				p.To.Sym = retjmp
   786  				p.Spadj = +ctxt.Autosize
   787  				break
   788  			}
   789  
   790  			p.As = obj.ARET
   791  			p.To.Type = obj.TYPE_MEM
   792  			p.To.Offset = 0
   793  			p.To.Reg = REGLINK
   794  			p.Spadj = +ctxt.Autosize
   795  
   796  		case AADD, ASUB:
   797  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   798  				if p.As == AADD {
   799  					p.Spadj = int32(-p.From.Offset)
   800  				} else {
   801  					p.Spadj = int32(+p.From.Offset)
   802  				}
   803  			}
   804  			break
   805  		}
   806  	}
   807  }
   808  
   809  func nocache(p *obj.Prog) {
   810  	p.Optab = 0
   811  	p.From.Class = 0
   812  	p.To.Class = 0
   813  }
   814  
   815  var unaryDst = map[int]bool{
   816  	AWORD:  true,
   817  	ADWORD: true,
   818  	ABL:    true,
   819  	AB:     true,
   820  	ASVC:   true,
   821  }
   822  
   823  var Linkarm64 = obj.LinkArch{
   824  	ByteOrder:  binary.LittleEndian,
   825  	Name:       "arm64",
   826  	Thechar:    '7',
   827  	Preprocess: preprocess,
   828  	Assemble:   span7,
   829  	Follow:     follow,
   830  	Progedit:   progedit,
   831  	UnaryDst:   unaryDst,
   832  	Minlc:      4,
   833  	Ptrsize:    8,
   834  	Regsize:    8,
   835  }