github.com/huandu/go@v0.0.0-20151114150818-04e615e41150/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 && 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  }