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