github.com/mdempsky/go@v0.0.0-20151201204031-5dd372bd1e70/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.Sym.Local = true
   254  			p.From.Name = obj.NAME_EXTERN
   255  			p.From.Offset = 0
   256  		}
   257  
   258  	case AFMOVD:
   259  		if p.From.Type == obj.TYPE_FCONST {
   260  			i64 := math.Float64bits(p.From.Val.(float64))
   261  			literal := fmt.Sprintf("$f64.%016x", uint64(i64))
   262  			s := obj.Linklookup(ctxt, literal, 0)
   263  			s.Size = 8
   264  			p.From.Type = obj.TYPE_MEM
   265  			p.From.Sym = s
   266  			p.From.Sym.Local = true
   267  			p.From.Name = obj.NAME_EXTERN
   268  			p.From.Offset = 0
   269  		}
   270  
   271  		break
   272  	}
   273  
   274  	// Rewrite negative immediates as positive immediates with
   275  	// complementary instruction.
   276  	switch p.As {
   277  	case AADD,
   278  		AADDW,
   279  		ASUB,
   280  		ASUBW,
   281  		ACMP,
   282  		ACMPW,
   283  		ACMN,
   284  		ACMNW:
   285  		if p.From.Type == obj.NAME_EXTERN && p.From.Offset < 0 {
   286  			p.From.Offset = -p.From.Offset
   287  			p.As = complements[p.As]
   288  		}
   289  
   290  		break
   291  	}
   292  
   293  	if ctxt.Flag_dynlink {
   294  		rewriteToUseGot(ctxt, p)
   295  	}
   296  }
   297  
   298  // Rewrite p, if necessary, to access global data via the global offset table.
   299  func rewriteToUseGot(ctxt *obj.Link, p *obj.Prog) {
   300  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
   301  		//     ADUFFxxx $offset
   302  		// becomes
   303  		//     MOVD runtime.duffxxx@GOT, REGTMP
   304  		//     ADD $offset, REGTMP
   305  		//     CALL REGTMP
   306  		var sym *obj.LSym
   307  		if p.As == obj.ADUFFZERO {
   308  			sym = obj.Linklookup(ctxt, "runtime.duffzero", 0)
   309  		} else {
   310  			sym = obj.Linklookup(ctxt, "runtime.duffcopy", 0)
   311  		}
   312  		offset := p.To.Offset
   313  		p.As = AMOVD
   314  		p.From.Type = obj.TYPE_MEM
   315  		p.From.Name = obj.NAME_GOTREF
   316  		p.From.Sym = sym
   317  		p.To.Type = obj.TYPE_REG
   318  		p.To.Reg = REGTMP
   319  		p.To.Name = obj.NAME_NONE
   320  		p.To.Offset = 0
   321  		p.To.Sym = nil
   322  		p1 := obj.Appendp(ctxt, p)
   323  		p1.As = AADD
   324  		p1.From.Type = obj.TYPE_CONST
   325  		p1.From.Offset = offset
   326  		p1.To.Type = obj.TYPE_REG
   327  		p1.To.Reg = REGTMP
   328  		p2 := obj.Appendp(ctxt, p1)
   329  		p2.As = obj.ACALL
   330  		p2.To.Type = obj.TYPE_REG
   331  		p2.To.Reg = REGTMP
   332  	}
   333  
   334  	// We only care about global data: NAME_EXTERN means a global
   335  	// symbol in the Go sense, and p.Sym.Local is true for a few
   336  	// internally defined symbols.
   337  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   338  		// MOVD $sym, Rx becomes MOVD sym@GOT, Rx
   339  		// MOVD $sym+<off>, Rx becomes MOVD sym@GOT, Rx; ADD <off>, Rx
   340  		if p.As != AMOVD {
   341  			ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
   342  		}
   343  		if p.To.Type != obj.TYPE_REG {
   344  			ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
   345  		}
   346  		p.From.Type = obj.TYPE_MEM
   347  		p.From.Name = obj.NAME_GOTREF
   348  		if p.From.Offset != 0 {
   349  			q := obj.Appendp(ctxt, p)
   350  			q.As = AADD
   351  			q.From.Type = obj.TYPE_CONST
   352  			q.From.Offset = p.From.Offset
   353  			q.To = p.To
   354  			p.From.Offset = 0
   355  		}
   356  	}
   357  	if p.From3 != nil && p.From3.Name == obj.NAME_EXTERN {
   358  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   359  	}
   360  	var source *obj.Addr
   361  	// MOVx sym, Ry becomes MOVD sym@GOT, REGTMP; MOVx (REGTMP), Ry
   362  	// MOVx Ry, sym becomes MOVD sym@GOT, REGTMP; MOVD Ry, (REGTMP)
   363  	// An addition may be inserted between the two MOVs if there is an offset.
   364  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local {
   365  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   366  			ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
   367  		}
   368  		source = &p.From
   369  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local {
   370  		source = &p.To
   371  	} else {
   372  		return
   373  	}
   374  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
   375  		return
   376  	}
   377  	if source.Sym.Type == obj.STLSBSS {
   378  		return
   379  	}
   380  	if source.Type != obj.TYPE_MEM {
   381  		ctxt.Diag("don't know how to handle %v with -dynlink", p)
   382  	}
   383  	p1 := obj.Appendp(ctxt, p)
   384  	p2 := obj.Appendp(ctxt, p1)
   385  	p1.As = AMOVD
   386  	p1.From.Type = obj.TYPE_MEM
   387  	p1.From.Sym = source.Sym
   388  	p1.From.Name = obj.NAME_GOTREF
   389  	p1.To.Type = obj.TYPE_REG
   390  	p1.To.Reg = REGTMP
   391  
   392  	p2.As = p.As
   393  	p2.From = p.From
   394  	p2.To = p.To
   395  	if p.From.Name == obj.NAME_EXTERN {
   396  		p2.From.Reg = REGTMP
   397  		p2.From.Name = obj.NAME_NONE
   398  		p2.From.Sym = nil
   399  	} else if p.To.Name == obj.NAME_EXTERN {
   400  		p2.To.Reg = REGTMP
   401  		p2.To.Name = obj.NAME_NONE
   402  		p2.To.Sym = nil
   403  	} else {
   404  		return
   405  	}
   406  	obj.Nopout(p)
   407  }
   408  
   409  func follow(ctxt *obj.Link, s *obj.LSym) {
   410  	ctxt.Cursym = s
   411  
   412  	firstp := ctxt.NewProg()
   413  	lastp := firstp
   414  	xfol(ctxt, s.Text, &lastp)
   415  	lastp.Link = nil
   416  	s.Text = firstp.Link
   417  }
   418  
   419  func relinv(a int) int {
   420  	switch a {
   421  	case ABEQ:
   422  		return ABNE
   423  	case ABNE:
   424  		return ABEQ
   425  	case ABCS:
   426  		return ABCC
   427  	case ABHS:
   428  		return ABLO
   429  	case ABCC:
   430  		return ABCS
   431  	case ABLO:
   432  		return ABHS
   433  	case ABMI:
   434  		return ABPL
   435  	case ABPL:
   436  		return ABMI
   437  	case ABVS:
   438  		return ABVC
   439  	case ABVC:
   440  		return ABVS
   441  	case ABHI:
   442  		return ABLS
   443  	case ABLS:
   444  		return ABHI
   445  	case ABGE:
   446  		return ABLT
   447  	case ABLT:
   448  		return ABGE
   449  	case ABGT:
   450  		return ABLE
   451  	case ABLE:
   452  		return ABGT
   453  	}
   454  
   455  	log.Fatalf("unknown relation: %s", Anames[a])
   456  	return 0
   457  }
   458  
   459  func xfol(ctxt *obj.Link, p *obj.Prog, last **obj.Prog) {
   460  	var q *obj.Prog
   461  	var r *obj.Prog
   462  	var a int
   463  	var i int
   464  
   465  loop:
   466  	if p == nil {
   467  		return
   468  	}
   469  	a = int(p.As)
   470  	if a == AB {
   471  		q = p.Pcond
   472  		if q != nil {
   473  			p.Mark |= FOLL
   474  			p = q
   475  			if !(p.Mark&FOLL != 0) {
   476  				goto loop
   477  			}
   478  		}
   479  	}
   480  
   481  	if p.Mark&FOLL != 0 {
   482  		i = 0
   483  		q = p
   484  		for ; i < 4; i, q = i+1, q.Link {
   485  			if q == *last || q == nil {
   486  				break
   487  			}
   488  			a = int(q.As)
   489  			if a == obj.ANOP {
   490  				i--
   491  				continue
   492  			}
   493  
   494  			if a == AB || a == obj.ARET || a == AERET {
   495  				goto copy
   496  			}
   497  			if q.Pcond == nil || (q.Pcond.Mark&FOLL != 0) {
   498  				continue
   499  			}
   500  			if a != ABEQ && a != ABNE {
   501  				continue
   502  			}
   503  
   504  		copy:
   505  			for {
   506  				r = ctxt.NewProg()
   507  				*r = *p
   508  				if !(r.Mark&FOLL != 0) {
   509  					fmt.Printf("cant happen 1\n")
   510  				}
   511  				r.Mark |= FOLL
   512  				if p != q {
   513  					p = p.Link
   514  					(*last).Link = r
   515  					*last = r
   516  					continue
   517  				}
   518  
   519  				(*last).Link = r
   520  				*last = r
   521  				if a == AB || a == obj.ARET || a == AERET {
   522  					return
   523  				}
   524  				if a == ABNE {
   525  					r.As = ABEQ
   526  				} else {
   527  					r.As = ABNE
   528  				}
   529  				r.Pcond = p.Link
   530  				r.Link = p.Pcond
   531  				if !(r.Link.Mark&FOLL != 0) {
   532  					xfol(ctxt, r.Link, last)
   533  				}
   534  				if !(r.Pcond.Mark&FOLL != 0) {
   535  					fmt.Printf("cant happen 2\n")
   536  				}
   537  				return
   538  			}
   539  		}
   540  
   541  		a = AB
   542  		q = ctxt.NewProg()
   543  		q.As = int16(a)
   544  		q.Lineno = p.Lineno
   545  		q.To.Type = obj.TYPE_BRANCH
   546  		q.To.Offset = p.Pc
   547  		q.Pcond = p
   548  		p = q
   549  	}
   550  
   551  	p.Mark |= FOLL
   552  	(*last).Link = p
   553  	*last = p
   554  	if a == AB || a == obj.ARET || a == AERET {
   555  		return
   556  	}
   557  	if p.Pcond != nil {
   558  		if a != ABL && p.Link != nil {
   559  			q = obj.Brchain(ctxt, p.Link)
   560  			if a != obj.ATEXT {
   561  				if q != nil && (q.Mark&FOLL != 0) {
   562  					p.As = int16(relinv(a))
   563  					p.Link = p.Pcond
   564  					p.Pcond = q
   565  				}
   566  			}
   567  
   568  			xfol(ctxt, p.Link, last)
   569  			q = obj.Brchain(ctxt, p.Pcond)
   570  			if q == nil {
   571  				q = p.Pcond
   572  			}
   573  			if q.Mark&FOLL != 0 {
   574  				p.Pcond = q
   575  				return
   576  			}
   577  
   578  			p = q
   579  			goto loop
   580  		}
   581  	}
   582  
   583  	p = p.Link
   584  	goto loop
   585  }
   586  
   587  func preprocess(ctxt *obj.Link, cursym *obj.LSym) {
   588  	ctxt.Cursym = cursym
   589  
   590  	if cursym.Text == nil || cursym.Text.Link == nil {
   591  		return
   592  	}
   593  
   594  	p := cursym.Text
   595  	textstksiz := p.To.Offset
   596  	aoffset := int32(textstksiz)
   597  
   598  	cursym.Args = p.To.Val.(int32)
   599  	cursym.Locals = int32(textstksiz)
   600  
   601  	/*
   602  	 * find leaf subroutines
   603  	 * strip NOPs
   604  	 * expand RET
   605  	 */
   606  	ctxt.Bso.Flush()
   607  	q := (*obj.Prog)(nil)
   608  	var q1 *obj.Prog
   609  	for p := cursym.Text; p != nil; p = p.Link {
   610  		switch p.As {
   611  		case obj.ATEXT:
   612  			p.Mark |= LEAF
   613  
   614  		case obj.ARET:
   615  			break
   616  
   617  		case obj.ANOP:
   618  			q1 = p.Link
   619  			q.Link = q1 /* q is non-nop */
   620  			q1.Mark |= p.Mark
   621  			continue
   622  
   623  		case ABL,
   624  			obj.ADUFFZERO,
   625  			obj.ADUFFCOPY:
   626  			cursym.Text.Mark &^= LEAF
   627  			fallthrough
   628  
   629  		case ACBNZ,
   630  			ACBZ,
   631  			ACBNZW,
   632  			ACBZW,
   633  			ATBZ,
   634  			ATBNZ,
   635  			AB,
   636  			ABEQ,
   637  			ABNE,
   638  			ABCS,
   639  			ABHS,
   640  			ABCC,
   641  			ABLO,
   642  			ABMI,
   643  			ABPL,
   644  			ABVS,
   645  			ABVC,
   646  			ABHI,
   647  			ABLS,
   648  			ABGE,
   649  			ABLT,
   650  			ABGT,
   651  			ABLE,
   652  			AADR, /* strange */
   653  			AADRP:
   654  			q1 = p.Pcond
   655  
   656  			if q1 != nil {
   657  				for q1.As == obj.ANOP {
   658  					q1 = q1.Link
   659  					p.Pcond = q1
   660  				}
   661  			}
   662  
   663  			break
   664  		}
   665  
   666  		q = p
   667  	}
   668  
   669  	var o int
   670  	var q2 *obj.Prog
   671  	var retjmp *obj.LSym
   672  	for p := cursym.Text; p != nil; p = p.Link {
   673  		o = int(p.As)
   674  		switch o {
   675  		case obj.ATEXT:
   676  			cursym.Text = p
   677  			if textstksiz < 0 {
   678  				ctxt.Autosize = 0
   679  			} else {
   680  				ctxt.Autosize = int32(textstksiz + 8)
   681  			}
   682  			if (cursym.Text.Mark&LEAF != 0) && ctxt.Autosize <= 8 {
   683  				ctxt.Autosize = 0
   684  			} else if ctxt.Autosize&(16-1) != 0 {
   685  				// The frame includes an LR.
   686  				// If the frame size is 8, it's only an LR,
   687  				// so there's no potential for breaking references to
   688  				// local variables by growing the frame size,
   689  				// because there are no local variables.
   690  				// But otherwise, if there is a non-empty locals section,
   691  				// the author of the code is responsible for making sure
   692  				// that the frame size is 8 mod 16.
   693  				if ctxt.Autosize == 8 {
   694  					ctxt.Autosize += 8
   695  					cursym.Locals += 8
   696  				} else {
   697  					ctxt.Diag("%v: unaligned frame size %d - must be 8 mod 16 (or 0)", p, ctxt.Autosize-8)
   698  				}
   699  			}
   700  			p.To.Offset = int64(ctxt.Autosize) - 8
   701  			if ctxt.Autosize == 0 && !(cursym.Text.Mark&LEAF != 0) {
   702  				if ctxt.Debugvlog != 0 {
   703  					fmt.Fprintf(ctxt.Bso, "save suppressed in: %s\n", cursym.Text.From.Sym.Name)
   704  				}
   705  				ctxt.Bso.Flush()
   706  				cursym.Text.Mark |= LEAF
   707  			}
   708  
   709  			if !(p.From3.Offset&obj.NOSPLIT != 0) {
   710  				p = stacksplit(ctxt, p, ctxt.Autosize) // emit split check
   711  			}
   712  
   713  			aoffset = ctxt.Autosize
   714  			if aoffset > 0xF0 {
   715  				aoffset = 0xF0
   716  			}
   717  			if cursym.Text.Mark&LEAF != 0 {
   718  				cursym.Leaf = 1
   719  				if ctxt.Autosize == 0 {
   720  					break
   721  				}
   722  				aoffset = 0
   723  			}
   724  
   725  			q = p
   726  			if ctxt.Autosize > aoffset {
   727  				q = ctxt.NewProg()
   728  				q.As = ASUB
   729  				q.Lineno = p.Lineno
   730  				q.From.Type = obj.TYPE_CONST
   731  				q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   732  				q.To.Type = obj.TYPE_REG
   733  				q.To.Reg = REGSP
   734  				q.Spadj = int32(q.From.Offset)
   735  				q.Link = p.Link
   736  				p.Link = q
   737  				if cursym.Text.Mark&LEAF != 0 {
   738  					break
   739  				}
   740  			}
   741  
   742  			q1 = ctxt.NewProg()
   743  			q1.As = AMOVD
   744  			q1.Lineno = p.Lineno
   745  			q1.From.Type = obj.TYPE_REG
   746  			q1.From.Reg = REGLINK
   747  			q1.To.Type = obj.TYPE_MEM
   748  			q1.Scond = C_XPRE
   749  			q1.To.Offset = int64(-aoffset)
   750  			q1.To.Reg = REGSP
   751  			q1.Link = q.Link
   752  			q1.Spadj = aoffset
   753  			q.Link = q1
   754  
   755  			if cursym.Text.From3.Offset&obj.WRAPPER != 0 {
   756  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
   757  				//
   758  				//	MOV g_panic(g), R1
   759  				//	CMP ZR, R1
   760  				//	BEQ end
   761  				//	MOV panic_argp(R1), R2
   762  				//	ADD $(autosize+8), RSP, R3
   763  				//	CMP R2, R3
   764  				//	BNE end
   765  				//	ADD $8, RSP, R4
   766  				//	MOVD R4, panic_argp(R1)
   767  				// end:
   768  				//	NOP
   769  				//
   770  				// The NOP is needed to give the jumps somewhere to land.
   771  				// It is a liblink NOP, not a ARM64 NOP: it encodes to 0 instruction bytes.
   772  				q = q1
   773  
   774  				q = obj.Appendp(ctxt, q)
   775  				q.As = AMOVD
   776  				q.From.Type = obj.TYPE_MEM
   777  				q.From.Reg = REGG
   778  				q.From.Offset = 4 * int64(ctxt.Arch.Ptrsize) // G.panic
   779  				q.To.Type = obj.TYPE_REG
   780  				q.To.Reg = REG_R1
   781  
   782  				q = obj.Appendp(ctxt, q)
   783  				q.As = ACMP
   784  				q.From.Type = obj.TYPE_REG
   785  				q.From.Reg = REGZERO
   786  				q.Reg = REG_R1
   787  
   788  				q = obj.Appendp(ctxt, q)
   789  				q.As = ABEQ
   790  				q.To.Type = obj.TYPE_BRANCH
   791  				q1 = q
   792  
   793  				q = obj.Appendp(ctxt, q)
   794  				q.As = AMOVD
   795  				q.From.Type = obj.TYPE_MEM
   796  				q.From.Reg = REG_R1
   797  				q.From.Offset = 0 // Panic.argp
   798  				q.To.Type = obj.TYPE_REG
   799  				q.To.Reg = REG_R2
   800  
   801  				q = obj.Appendp(ctxt, q)
   802  				q.As = AADD
   803  				q.From.Type = obj.TYPE_CONST
   804  				q.From.Offset = int64(ctxt.Autosize) + 8
   805  				q.Reg = REGSP
   806  				q.To.Type = obj.TYPE_REG
   807  				q.To.Reg = REG_R3
   808  
   809  				q = obj.Appendp(ctxt, q)
   810  				q.As = ACMP
   811  				q.From.Type = obj.TYPE_REG
   812  				q.From.Reg = REG_R2
   813  				q.Reg = REG_R3
   814  
   815  				q = obj.Appendp(ctxt, q)
   816  				q.As = ABNE
   817  				q.To.Type = obj.TYPE_BRANCH
   818  				q2 = q
   819  
   820  				q = obj.Appendp(ctxt, q)
   821  				q.As = AADD
   822  				q.From.Type = obj.TYPE_CONST
   823  				q.From.Offset = 8
   824  				q.Reg = REGSP
   825  				q.To.Type = obj.TYPE_REG
   826  				q.To.Reg = REG_R4
   827  
   828  				q = obj.Appendp(ctxt, q)
   829  				q.As = AMOVD
   830  				q.From.Type = obj.TYPE_REG
   831  				q.From.Reg = REG_R4
   832  				q.To.Type = obj.TYPE_MEM
   833  				q.To.Reg = REG_R1
   834  				q.To.Offset = 0 // Panic.argp
   835  
   836  				q = obj.Appendp(ctxt, q)
   837  
   838  				q.As = obj.ANOP
   839  				q1.Pcond = q
   840  				q2.Pcond = q
   841  			}
   842  
   843  		case obj.ARET:
   844  			nocache(p)
   845  			if p.From.Type == obj.TYPE_CONST {
   846  				ctxt.Diag("using BECOME (%v) is not supported!", p)
   847  				break
   848  			}
   849  
   850  			retjmp = p.To.Sym
   851  			p.To = obj.Addr{}
   852  			if cursym.Text.Mark&LEAF != 0 {
   853  				if ctxt.Autosize != 0 {
   854  					p.As = AADD
   855  					p.From.Type = obj.TYPE_CONST
   856  					p.From.Offset = int64(ctxt.Autosize)
   857  					p.To.Type = obj.TYPE_REG
   858  					p.To.Reg = REGSP
   859  					p.Spadj = -ctxt.Autosize
   860  				}
   861  			} else {
   862  				/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
   863  				aoffset = ctxt.Autosize
   864  
   865  				if aoffset > 0xF0 {
   866  					aoffset = 0xF0
   867  				}
   868  				p.As = AMOVD
   869  				p.From.Type = obj.TYPE_MEM
   870  				p.Scond = C_XPOST
   871  				p.From.Offset = int64(aoffset)
   872  				p.From.Reg = REGSP
   873  				p.To.Type = obj.TYPE_REG
   874  				p.To.Reg = REGLINK
   875  				p.Spadj = -aoffset
   876  				if ctxt.Autosize > aoffset {
   877  					q = ctxt.NewProg()
   878  					q.As = AADD
   879  					q.From.Type = obj.TYPE_CONST
   880  					q.From.Offset = int64(ctxt.Autosize) - int64(aoffset)
   881  					q.To.Type = obj.TYPE_REG
   882  					q.To.Reg = REGSP
   883  					q.Link = p.Link
   884  					q.Spadj = int32(-q.From.Offset)
   885  					q.Lineno = p.Lineno
   886  					p.Link = q
   887  					p = q
   888  				}
   889  			}
   890  
   891  			if p.As != obj.ARET {
   892  				q = ctxt.NewProg()
   893  				q.Lineno = p.Lineno
   894  				q.Link = p.Link
   895  				p.Link = q
   896  				p = q
   897  			}
   898  
   899  			if retjmp != nil { // retjmp
   900  				p.As = AB
   901  				p.To.Type = obj.TYPE_BRANCH
   902  				p.To.Sym = retjmp
   903  				p.Spadj = +ctxt.Autosize
   904  				break
   905  			}
   906  
   907  			p.As = obj.ARET
   908  			p.To.Type = obj.TYPE_MEM
   909  			p.To.Offset = 0
   910  			p.To.Reg = REGLINK
   911  			p.Spadj = +ctxt.Autosize
   912  
   913  		case AADD, ASUB:
   914  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
   915  				if p.As == AADD {
   916  					p.Spadj = int32(-p.From.Offset)
   917  				} else {
   918  					p.Spadj = int32(+p.From.Offset)
   919  				}
   920  			}
   921  			break
   922  		}
   923  	}
   924  }
   925  
   926  func nocache(p *obj.Prog) {
   927  	p.Optab = 0
   928  	p.From.Class = 0
   929  	p.To.Class = 0
   930  }
   931  
   932  var unaryDst = map[int]bool{
   933  	AWORD:  true,
   934  	ADWORD: true,
   935  	ABL:    true,
   936  	AB:     true,
   937  	ASVC:   true,
   938  }
   939  
   940  var Linkarm64 = obj.LinkArch{
   941  	ByteOrder:  binary.LittleEndian,
   942  	Name:       "arm64",
   943  	Thechar:    '7',
   944  	Preprocess: preprocess,
   945  	Assemble:   span7,
   946  	Follow:     follow,
   947  	Progedit:   progedit,
   948  	UnaryDst:   unaryDst,
   949  	Minlc:      4,
   950  	Ptrsize:    8,
   951  	Regsize:    8,
   952  }