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