obj5.go raw

   1  // Derived from Inferno utils/5c/swt.c
   2  // https://bitbucket.org/inferno-os/inferno-os/src/master/utils/5c/swt.c
   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 arm
  32  
  33  import (
  34  	"github.com/twitchyliquid64/golang-asm/obj"
  35  	"github.com/twitchyliquid64/golang-asm/objabi"
  36  	"github.com/twitchyliquid64/golang-asm/sys"
  37  )
  38  
  39  var progedit_tlsfallback *obj.LSym
  40  
  41  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
  42  	p.From.Class = 0
  43  	p.To.Class = 0
  44  
  45  	c := ctxt5{ctxt: ctxt, newprog: newprog}
  46  
  47  	// Rewrite B/BL to symbol as TYPE_BRANCH.
  48  	switch p.As {
  49  	case AB, ABL, obj.ADUFFZERO, obj.ADUFFCOPY:
  50  		if p.To.Type == obj.TYPE_MEM && (p.To.Name == obj.NAME_EXTERN || p.To.Name == obj.NAME_STATIC) && p.To.Sym != nil {
  51  			p.To.Type = obj.TYPE_BRANCH
  52  		}
  53  	}
  54  
  55  	// Replace TLS register fetches on older ARM processors.
  56  	switch p.As {
  57  	// Treat MRC 15, 0, <reg>, C13, C0, 3 specially.
  58  	case AMRC:
  59  		if p.To.Offset&0xffff0fff == 0xee1d0f70 {
  60  			// Because the instruction might be rewritten to a BL which returns in R0
  61  			// the register must be zero.
  62  			if p.To.Offset&0xf000 != 0 {
  63  				ctxt.Diag("%v: TLS MRC instruction must write to R0 as it might get translated into a BL instruction", p.Line())
  64  			}
  65  
  66  			if objabi.GOARM < 7 {
  67  				// Replace it with BL runtime.read_tls_fallback(SB) for ARM CPUs that lack the tls extension.
  68  				if progedit_tlsfallback == nil {
  69  					progedit_tlsfallback = ctxt.Lookup("runtime.read_tls_fallback")
  70  				}
  71  
  72  				// MOVW	LR, R11
  73  				p.As = AMOVW
  74  
  75  				p.From.Type = obj.TYPE_REG
  76  				p.From.Reg = REGLINK
  77  				p.To.Type = obj.TYPE_REG
  78  				p.To.Reg = REGTMP
  79  
  80  				// BL	runtime.read_tls_fallback(SB)
  81  				p = obj.Appendp(p, newprog)
  82  
  83  				p.As = ABL
  84  				p.To.Type = obj.TYPE_BRANCH
  85  				p.To.Sym = progedit_tlsfallback
  86  				p.To.Offset = 0
  87  
  88  				// MOVW	R11, LR
  89  				p = obj.Appendp(p, newprog)
  90  
  91  				p.As = AMOVW
  92  				p.From.Type = obj.TYPE_REG
  93  				p.From.Reg = REGTMP
  94  				p.To.Type = obj.TYPE_REG
  95  				p.To.Reg = REGLINK
  96  				break
  97  			}
  98  		}
  99  
 100  		// Otherwise, MRC/MCR instructions need no further treatment.
 101  		p.As = AWORD
 102  	}
 103  
 104  	// Rewrite float constants to values stored in memory.
 105  	switch p.As {
 106  	case AMOVF:
 107  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
 108  			f32 := float32(p.From.Val.(float64))
 109  			p.From.Type = obj.TYPE_MEM
 110  			p.From.Sym = ctxt.Float32Sym(f32)
 111  			p.From.Name = obj.NAME_EXTERN
 112  			p.From.Offset = 0
 113  		}
 114  
 115  	case AMOVD:
 116  		if p.From.Type == obj.TYPE_FCONST && c.chipfloat5(p.From.Val.(float64)) < 0 && (c.chipzero5(p.From.Val.(float64)) < 0 || p.Scond&C_SCOND != C_SCOND_NONE) {
 117  			p.From.Type = obj.TYPE_MEM
 118  			p.From.Sym = ctxt.Float64Sym(p.From.Val.(float64))
 119  			p.From.Name = obj.NAME_EXTERN
 120  			p.From.Offset = 0
 121  		}
 122  	}
 123  
 124  	if ctxt.Flag_dynlink {
 125  		c.rewriteToUseGot(p)
 126  	}
 127  }
 128  
 129  // Rewrite p, if necessary, to access global data via the global offset table.
 130  func (c *ctxt5) rewriteToUseGot(p *obj.Prog) {
 131  	if p.As == obj.ADUFFCOPY || p.As == obj.ADUFFZERO {
 132  		//     ADUFFxxx $offset
 133  		// becomes
 134  		//     MOVW runtime.duffxxx@GOT, R9
 135  		//     ADD $offset, R9
 136  		//     CALL (R9)
 137  		var sym *obj.LSym
 138  		if p.As == obj.ADUFFZERO {
 139  			sym = c.ctxt.Lookup("runtime.duffzero")
 140  		} else {
 141  			sym = c.ctxt.Lookup("runtime.duffcopy")
 142  		}
 143  		offset := p.To.Offset
 144  		p.As = AMOVW
 145  		p.From.Type = obj.TYPE_MEM
 146  		p.From.Name = obj.NAME_GOTREF
 147  		p.From.Sym = sym
 148  		p.To.Type = obj.TYPE_REG
 149  		p.To.Reg = REG_R9
 150  		p.To.Name = obj.NAME_NONE
 151  		p.To.Offset = 0
 152  		p.To.Sym = nil
 153  		p1 := obj.Appendp(p, c.newprog)
 154  		p1.As = AADD
 155  		p1.From.Type = obj.TYPE_CONST
 156  		p1.From.Offset = offset
 157  		p1.To.Type = obj.TYPE_REG
 158  		p1.To.Reg = REG_R9
 159  		p2 := obj.Appendp(p1, c.newprog)
 160  		p2.As = obj.ACALL
 161  		p2.To.Type = obj.TYPE_MEM
 162  		p2.To.Reg = REG_R9
 163  		return
 164  	}
 165  
 166  	// We only care about global data: NAME_EXTERN means a global
 167  	// symbol in the Go sense, and p.Sym.Local is true for a few
 168  	// internally defined symbols.
 169  	if p.From.Type == obj.TYPE_ADDR && p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 170  		// MOVW $sym, Rx becomes MOVW sym@GOT, Rx
 171  		// MOVW $sym+<off>, Rx becomes MOVW sym@GOT, Rx; ADD <off>, Rx
 172  		if p.As != AMOVW {
 173  			c.ctxt.Diag("do not know how to handle TYPE_ADDR in %v with -dynlink", p)
 174  		}
 175  		if p.To.Type != obj.TYPE_REG {
 176  			c.ctxt.Diag("do not know how to handle LEAQ-type insn to non-register in %v with -dynlink", p)
 177  		}
 178  		p.From.Type = obj.TYPE_MEM
 179  		p.From.Name = obj.NAME_GOTREF
 180  		if p.From.Offset != 0 {
 181  			q := obj.Appendp(p, c.newprog)
 182  			q.As = AADD
 183  			q.From.Type = obj.TYPE_CONST
 184  			q.From.Offset = p.From.Offset
 185  			q.To = p.To
 186  			p.From.Offset = 0
 187  		}
 188  	}
 189  	if p.GetFrom3() != nil && p.GetFrom3().Name == obj.NAME_EXTERN {
 190  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
 191  	}
 192  	var source *obj.Addr
 193  	// MOVx sym, Ry becomes MOVW sym@GOT, R9; MOVx (R9), Ry
 194  	// MOVx Ry, sym becomes MOVW sym@GOT, R9; MOVx Ry, (R9)
 195  	// An addition may be inserted between the two MOVs if there is an offset.
 196  	if p.From.Name == obj.NAME_EXTERN && !p.From.Sym.Local() {
 197  		if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 198  			c.ctxt.Diag("cannot handle NAME_EXTERN on both sides in %v with -dynlink", p)
 199  		}
 200  		source = &p.From
 201  	} else if p.To.Name == obj.NAME_EXTERN && !p.To.Sym.Local() {
 202  		source = &p.To
 203  	} else {
 204  		return
 205  	}
 206  	if p.As == obj.ATEXT || p.As == obj.AFUNCDATA || p.As == obj.ACALL || p.As == obj.ARET || p.As == obj.AJMP {
 207  		return
 208  	}
 209  	if source.Sym.Type == objabi.STLSBSS {
 210  		return
 211  	}
 212  	if source.Type != obj.TYPE_MEM {
 213  		c.ctxt.Diag("don't know how to handle %v with -dynlink", p)
 214  	}
 215  	p1 := obj.Appendp(p, c.newprog)
 216  	p2 := obj.Appendp(p1, c.newprog)
 217  
 218  	p1.As = AMOVW
 219  	p1.From.Type = obj.TYPE_MEM
 220  	p1.From.Sym = source.Sym
 221  	p1.From.Name = obj.NAME_GOTREF
 222  	p1.To.Type = obj.TYPE_REG
 223  	p1.To.Reg = REG_R9
 224  
 225  	p2.As = p.As
 226  	p2.From = p.From
 227  	p2.To = p.To
 228  	if p.From.Name == obj.NAME_EXTERN {
 229  		p2.From.Reg = REG_R9
 230  		p2.From.Name = obj.NAME_NONE
 231  		p2.From.Sym = nil
 232  	} else if p.To.Name == obj.NAME_EXTERN {
 233  		p2.To.Reg = REG_R9
 234  		p2.To.Name = obj.NAME_NONE
 235  		p2.To.Sym = nil
 236  	} else {
 237  		return
 238  	}
 239  	obj.Nopout(p)
 240  }
 241  
 242  // Prog.mark
 243  const (
 244  	FOLL  = 1 << 0
 245  	LABEL = 1 << 1
 246  	LEAF  = 1 << 2
 247  )
 248  
 249  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 250  	autosize := int32(0)
 251  
 252  	if cursym.Func.Text == nil || cursym.Func.Text.Link == nil {
 253  		return
 254  	}
 255  
 256  	c := ctxt5{ctxt: ctxt, cursym: cursym, newprog: newprog}
 257  
 258  	p := c.cursym.Func.Text
 259  	autoffset := int32(p.To.Offset)
 260  	if autoffset == -4 {
 261  		// Historical way to mark NOFRAME.
 262  		p.From.Sym.Set(obj.AttrNoFrame, true)
 263  		autoffset = 0
 264  	}
 265  	if autoffset < 0 || autoffset%4 != 0 {
 266  		c.ctxt.Diag("frame size %d not 0 or a positive multiple of 4", autoffset)
 267  	}
 268  	if p.From.Sym.NoFrame() {
 269  		if autoffset != 0 {
 270  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", autoffset)
 271  		}
 272  	}
 273  
 274  	cursym.Func.Locals = autoffset
 275  	cursym.Func.Args = p.To.Val.(int32)
 276  
 277  	/*
 278  	 * find leaf subroutines
 279  	 */
 280  	for p := cursym.Func.Text; p != nil; p = p.Link {
 281  		switch p.As {
 282  		case obj.ATEXT:
 283  			p.Mark |= LEAF
 284  
 285  		case ADIV, ADIVU, AMOD, AMODU:
 286  			cursym.Func.Text.Mark &^= LEAF
 287  
 288  		case ABL,
 289  			ABX,
 290  			obj.ADUFFZERO,
 291  			obj.ADUFFCOPY:
 292  			cursym.Func.Text.Mark &^= LEAF
 293  		}
 294  	}
 295  
 296  	var q2 *obj.Prog
 297  	for p := cursym.Func.Text; p != nil; p = p.Link {
 298  		o := p.As
 299  		switch o {
 300  		case obj.ATEXT:
 301  			autosize = autoffset
 302  
 303  			if p.Mark&LEAF != 0 && autosize == 0 {
 304  				// A leaf function with no locals has no frame.
 305  				p.From.Sym.Set(obj.AttrNoFrame, true)
 306  			}
 307  
 308  			if !p.From.Sym.NoFrame() {
 309  				// If there is a stack frame at all, it includes
 310  				// space to save the LR.
 311  				autosize += 4
 312  			}
 313  
 314  			if autosize == 0 && cursym.Func.Text.Mark&LEAF == 0 {
 315  				// A very few functions that do not return to their caller
 316  				// are not identified as leaves but still have no frame.
 317  				if ctxt.Debugvlog {
 318  					ctxt.Logf("save suppressed in: %s\n", cursym.Name)
 319  				}
 320  
 321  				cursym.Func.Text.Mark |= LEAF
 322  			}
 323  
 324  			// FP offsets need an updated p.To.Offset.
 325  			p.To.Offset = int64(autosize) - 4
 326  
 327  			if cursym.Func.Text.Mark&LEAF != 0 {
 328  				cursym.Set(obj.AttrLeaf, true)
 329  				if p.From.Sym.NoFrame() {
 330  					break
 331  				}
 332  			}
 333  
 334  			if !p.From.Sym.NoSplit() {
 335  				p = c.stacksplit(p, autosize) // emit split check
 336  			}
 337  
 338  			// MOVW.W		R14,$-autosize(SP)
 339  			p = obj.Appendp(p, c.newprog)
 340  
 341  			p.As = AMOVW
 342  			p.Scond |= C_WBIT
 343  			p.From.Type = obj.TYPE_REG
 344  			p.From.Reg = REGLINK
 345  			p.To.Type = obj.TYPE_MEM
 346  			p.To.Offset = int64(-autosize)
 347  			p.To.Reg = REGSP
 348  			p.Spadj = autosize
 349  
 350  			if cursym.Func.Text.From.Sym.Wrapper() {
 351  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
 352  				//
 353  				//	MOVW g_panic(g), R1
 354  				//	CMP  $0, R1
 355  				//	B.NE checkargp
 356  				// end:
 357  				//	NOP
 358  				// ... function ...
 359  				// checkargp:
 360  				//	MOVW panic_argp(R1), R2
 361  				//	ADD  $(autosize+4), R13, R3
 362  				//	CMP  R2, R3
 363  				//	B.NE end
 364  				//	ADD  $4, R13, R4
 365  				//	MOVW R4, panic_argp(R1)
 366  				//	B    end
 367  				//
 368  				// The NOP is needed to give the jumps somewhere to land.
 369  				// It is a liblink NOP, not an ARM NOP: it encodes to 0 instruction bytes.
 370  
 371  				p = obj.Appendp(p, newprog)
 372  				p.As = AMOVW
 373  				p.From.Type = obj.TYPE_MEM
 374  				p.From.Reg = REGG
 375  				p.From.Offset = 4 * int64(ctxt.Arch.PtrSize) // G.panic
 376  				p.To.Type = obj.TYPE_REG
 377  				p.To.Reg = REG_R1
 378  
 379  				p = obj.Appendp(p, newprog)
 380  				p.As = ACMP
 381  				p.From.Type = obj.TYPE_CONST
 382  				p.From.Offset = 0
 383  				p.Reg = REG_R1
 384  
 385  				// B.NE checkargp
 386  				bne := obj.Appendp(p, newprog)
 387  				bne.As = ABNE
 388  				bne.To.Type = obj.TYPE_BRANCH
 389  
 390  				// end: NOP
 391  				end := obj.Appendp(bne, newprog)
 392  				end.As = obj.ANOP
 393  
 394  				// find end of function
 395  				var last *obj.Prog
 396  				for last = end; last.Link != nil; last = last.Link {
 397  				}
 398  
 399  				// MOVW panic_argp(R1), R2
 400  				mov := obj.Appendp(last, newprog)
 401  				mov.As = AMOVW
 402  				mov.From.Type = obj.TYPE_MEM
 403  				mov.From.Reg = REG_R1
 404  				mov.From.Offset = 0 // Panic.argp
 405  				mov.To.Type = obj.TYPE_REG
 406  				mov.To.Reg = REG_R2
 407  
 408  				// B.NE branch target is MOVW above
 409  				bne.To.SetTarget(mov)
 410  
 411  				// ADD $(autosize+4), R13, R3
 412  				p = obj.Appendp(mov, newprog)
 413  				p.As = AADD
 414  				p.From.Type = obj.TYPE_CONST
 415  				p.From.Offset = int64(autosize) + 4
 416  				p.Reg = REG_R13
 417  				p.To.Type = obj.TYPE_REG
 418  				p.To.Reg = REG_R3
 419  
 420  				// CMP R2, R3
 421  				p = obj.Appendp(p, newprog)
 422  				p.As = ACMP
 423  				p.From.Type = obj.TYPE_REG
 424  				p.From.Reg = REG_R2
 425  				p.Reg = REG_R3
 426  
 427  				// B.NE end
 428  				p = obj.Appendp(p, newprog)
 429  				p.As = ABNE
 430  				p.To.Type = obj.TYPE_BRANCH
 431  				p.To.SetTarget(end)
 432  
 433  				// ADD $4, R13, R4
 434  				p = obj.Appendp(p, newprog)
 435  				p.As = AADD
 436  				p.From.Type = obj.TYPE_CONST
 437  				p.From.Offset = 4
 438  				p.Reg = REG_R13
 439  				p.To.Type = obj.TYPE_REG
 440  				p.To.Reg = REG_R4
 441  
 442  				// MOVW R4, panic_argp(R1)
 443  				p = obj.Appendp(p, newprog)
 444  				p.As = AMOVW
 445  				p.From.Type = obj.TYPE_REG
 446  				p.From.Reg = REG_R4
 447  				p.To.Type = obj.TYPE_MEM
 448  				p.To.Reg = REG_R1
 449  				p.To.Offset = 0 // Panic.argp
 450  
 451  				// B end
 452  				p = obj.Appendp(p, newprog)
 453  				p.As = AB
 454  				p.To.Type = obj.TYPE_BRANCH
 455  				p.To.SetTarget(end)
 456  
 457  				// reset for subsequent passes
 458  				p = end
 459  			}
 460  
 461  		case obj.ARET:
 462  			nocache(p)
 463  			if cursym.Func.Text.Mark&LEAF != 0 {
 464  				if autosize == 0 {
 465  					p.As = AB
 466  					p.From = obj.Addr{}
 467  					if p.To.Sym != nil { // retjmp
 468  						p.To.Type = obj.TYPE_BRANCH
 469  					} else {
 470  						p.To.Type = obj.TYPE_MEM
 471  						p.To.Offset = 0
 472  						p.To.Reg = REGLINK
 473  					}
 474  
 475  					break
 476  				}
 477  			}
 478  
 479  			p.As = AMOVW
 480  			p.Scond |= C_PBIT
 481  			p.From.Type = obj.TYPE_MEM
 482  			p.From.Offset = int64(autosize)
 483  			p.From.Reg = REGSP
 484  			p.To.Type = obj.TYPE_REG
 485  			p.To.Reg = REGPC
 486  
 487  			// If there are instructions following
 488  			// this ARET, they come from a branch
 489  			// with the same stackframe, so no spadj.
 490  			if p.To.Sym != nil { // retjmp
 491  				p.To.Reg = REGLINK
 492  				q2 = obj.Appendp(p, newprog)
 493  				q2.As = AB
 494  				q2.To.Type = obj.TYPE_BRANCH
 495  				q2.To.Sym = p.To.Sym
 496  				p.To.Sym = nil
 497  				p = q2
 498  			}
 499  
 500  		case AADD:
 501  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
 502  				p.Spadj = int32(-p.From.Offset)
 503  			}
 504  
 505  		case ASUB:
 506  			if p.From.Type == obj.TYPE_CONST && p.From.Reg == 0 && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
 507  				p.Spadj = int32(p.From.Offset)
 508  			}
 509  
 510  		case ADIV, ADIVU, AMOD, AMODU:
 511  			if cursym.Func.Text.From.Sym.NoSplit() {
 512  				ctxt.Diag("cannot divide in NOSPLIT function")
 513  			}
 514  			const debugdivmod = false
 515  			if debugdivmod {
 516  				break
 517  			}
 518  			if p.From.Type != obj.TYPE_REG {
 519  				break
 520  			}
 521  			if p.To.Type != obj.TYPE_REG {
 522  				break
 523  			}
 524  
 525  			// Make copy because we overwrite p below.
 526  			q1 := *p
 527  			if q1.Reg == REGTMP || q1.Reg == 0 && q1.To.Reg == REGTMP {
 528  				ctxt.Diag("div already using REGTMP: %v", p)
 529  			}
 530  
 531  			/* MOV m(g),REGTMP */
 532  			p.As = AMOVW
 533  			p.Pos = q1.Pos
 534  			p.From.Type = obj.TYPE_MEM
 535  			p.From.Reg = REGG
 536  			p.From.Offset = 6 * 4 // offset of g.m
 537  			p.Reg = 0
 538  			p.To.Type = obj.TYPE_REG
 539  			p.To.Reg = REGTMP
 540  
 541  			/* MOV a,m_divmod(REGTMP) */
 542  			p = obj.Appendp(p, newprog)
 543  			p.As = AMOVW
 544  			p.Pos = q1.Pos
 545  			p.From.Type = obj.TYPE_REG
 546  			p.From.Reg = q1.From.Reg
 547  			p.To.Type = obj.TYPE_MEM
 548  			p.To.Reg = REGTMP
 549  			p.To.Offset = 8 * 4 // offset of m.divmod
 550  
 551  			/* MOV b, R8 */
 552  			p = obj.Appendp(p, newprog)
 553  			p.As = AMOVW
 554  			p.Pos = q1.Pos
 555  			p.From.Type = obj.TYPE_REG
 556  			p.From.Reg = q1.Reg
 557  			if q1.Reg == 0 {
 558  				p.From.Reg = q1.To.Reg
 559  			}
 560  			p.To.Type = obj.TYPE_REG
 561  			p.To.Reg = REG_R8
 562  			p.To.Offset = 0
 563  
 564  			/* CALL appropriate */
 565  			p = obj.Appendp(p, newprog)
 566  			p.As = ABL
 567  			p.Pos = q1.Pos
 568  			p.To.Type = obj.TYPE_BRANCH
 569  			switch o {
 570  			case ADIV:
 571  				p.To.Sym = symdiv
 572  			case ADIVU:
 573  				p.To.Sym = symdivu
 574  			case AMOD:
 575  				p.To.Sym = symmod
 576  			case AMODU:
 577  				p.To.Sym = symmodu
 578  			}
 579  
 580  			/* MOV REGTMP, b */
 581  			p = obj.Appendp(p, newprog)
 582  			p.As = AMOVW
 583  			p.Pos = q1.Pos
 584  			p.From.Type = obj.TYPE_REG
 585  			p.From.Reg = REGTMP
 586  			p.From.Offset = 0
 587  			p.To.Type = obj.TYPE_REG
 588  			p.To.Reg = q1.To.Reg
 589  
 590  		case AMOVW:
 591  			if (p.Scond&C_WBIT != 0) && p.To.Type == obj.TYPE_MEM && p.To.Reg == REGSP {
 592  				p.Spadj = int32(-p.To.Offset)
 593  			}
 594  			if (p.Scond&C_PBIT != 0) && p.From.Type == obj.TYPE_MEM && p.From.Reg == REGSP && p.To.Reg != REGPC {
 595  				p.Spadj = int32(-p.From.Offset)
 596  			}
 597  			if p.From.Type == obj.TYPE_ADDR && p.From.Reg == REGSP && p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP {
 598  				p.Spadj = int32(-p.From.Offset)
 599  			}
 600  
 601  		case obj.AGETCALLERPC:
 602  			if cursym.Leaf() {
 603  				/* MOVW LR, Rd */
 604  				p.As = AMOVW
 605  				p.From.Type = obj.TYPE_REG
 606  				p.From.Reg = REGLINK
 607  			} else {
 608  				/* MOVW (RSP), Rd */
 609  				p.As = AMOVW
 610  				p.From.Type = obj.TYPE_MEM
 611  				p.From.Reg = REGSP
 612  			}
 613  		}
 614  	}
 615  }
 616  
 617  func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
 618  	// MOVW g_stackguard(g), R1
 619  	p = obj.Appendp(p, c.newprog)
 620  
 621  	p.As = AMOVW
 622  	p.From.Type = obj.TYPE_MEM
 623  	p.From.Reg = REGG
 624  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
 625  	if c.cursym.CFunc() {
 626  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
 627  	}
 628  	p.To.Type = obj.TYPE_REG
 629  	p.To.Reg = REG_R1
 630  
 631  	// Mark the stack bound check and morestack call async nonpreemptible.
 632  	// If we get preempted here, when resumed the preemption request is
 633  	// cleared, but we'll still call morestack, which will double the stack
 634  	// unnecessarily. See issue #35470.
 635  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
 636  
 637  	if framesize <= objabi.StackSmall {
 638  		// small stack: SP < stackguard
 639  		//	CMP	stackguard, SP
 640  		p = obj.Appendp(p, c.newprog)
 641  
 642  		p.As = ACMP
 643  		p.From.Type = obj.TYPE_REG
 644  		p.From.Reg = REG_R1
 645  		p.Reg = REGSP
 646  	} else if framesize <= objabi.StackBig {
 647  		// large stack: SP-framesize < stackguard-StackSmall
 648  		//	MOVW $-(framesize-StackSmall)(SP), R2
 649  		//	CMP stackguard, R2
 650  		p = obj.Appendp(p, c.newprog)
 651  
 652  		p.As = AMOVW
 653  		p.From.Type = obj.TYPE_ADDR
 654  		p.From.Reg = REGSP
 655  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
 656  		p.To.Type = obj.TYPE_REG
 657  		p.To.Reg = REG_R2
 658  
 659  		p = obj.Appendp(p, c.newprog)
 660  		p.As = ACMP
 661  		p.From.Type = obj.TYPE_REG
 662  		p.From.Reg = REG_R1
 663  		p.Reg = REG_R2
 664  	} else {
 665  		// Such a large stack we need to protect against wraparound
 666  		// if SP is close to zero.
 667  		//	SP-stackguard+StackGuard < framesize + (StackGuard-StackSmall)
 668  		// The +StackGuard on both sides is required to keep the left side positive:
 669  		// SP is allowed to be slightly below stackguard. See stack.h.
 670  		//	CMP     $StackPreempt, R1
 671  		//	MOVW.NE $StackGuard(SP), R2
 672  		//	SUB.NE  R1, R2
 673  		//	MOVW.NE $(framesize+(StackGuard-StackSmall)), R3
 674  		//	CMP.NE  R3, R2
 675  		p = obj.Appendp(p, c.newprog)
 676  
 677  		p.As = ACMP
 678  		p.From.Type = obj.TYPE_CONST
 679  		p.From.Offset = int64(uint32(objabi.StackPreempt & (1<<32 - 1)))
 680  		p.Reg = REG_R1
 681  
 682  		p = obj.Appendp(p, c.newprog)
 683  		p.As = AMOVW
 684  		p.From.Type = obj.TYPE_ADDR
 685  		p.From.Reg = REGSP
 686  		p.From.Offset = int64(objabi.StackGuard)
 687  		p.To.Type = obj.TYPE_REG
 688  		p.To.Reg = REG_R2
 689  		p.Scond = C_SCOND_NE
 690  
 691  		p = obj.Appendp(p, c.newprog)
 692  		p.As = ASUB
 693  		p.From.Type = obj.TYPE_REG
 694  		p.From.Reg = REG_R1
 695  		p.To.Type = obj.TYPE_REG
 696  		p.To.Reg = REG_R2
 697  		p.Scond = C_SCOND_NE
 698  
 699  		p = obj.Appendp(p, c.newprog)
 700  		p.As = AMOVW
 701  		p.From.Type = obj.TYPE_ADDR
 702  		p.From.Offset = int64(framesize) + (int64(objabi.StackGuard) - objabi.StackSmall)
 703  		p.To.Type = obj.TYPE_REG
 704  		p.To.Reg = REG_R3
 705  		p.Scond = C_SCOND_NE
 706  
 707  		p = obj.Appendp(p, c.newprog)
 708  		p.As = ACMP
 709  		p.From.Type = obj.TYPE_REG
 710  		p.From.Reg = REG_R3
 711  		p.Reg = REG_R2
 712  		p.Scond = C_SCOND_NE
 713  	}
 714  
 715  	// BLS call-to-morestack
 716  	bls := obj.Appendp(p, c.newprog)
 717  	bls.As = ABLS
 718  	bls.To.Type = obj.TYPE_BRANCH
 719  
 720  	end := c.ctxt.EndUnsafePoint(bls, c.newprog, -1)
 721  
 722  	var last *obj.Prog
 723  	for last = c.cursym.Func.Text; last.Link != nil; last = last.Link {
 724  	}
 725  
 726  	// Now we are at the end of the function, but logically
 727  	// we are still in function prologue. We need to fix the
 728  	// SP data and PCDATA.
 729  	spfix := obj.Appendp(last, c.newprog)
 730  	spfix.As = obj.ANOP
 731  	spfix.Spadj = -framesize
 732  
 733  	pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
 734  	pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
 735  
 736  	// MOVW	LR, R3
 737  	movw := obj.Appendp(pcdata, c.newprog)
 738  	movw.As = AMOVW
 739  	movw.From.Type = obj.TYPE_REG
 740  	movw.From.Reg = REGLINK
 741  	movw.To.Type = obj.TYPE_REG
 742  	movw.To.Reg = REG_R3
 743  
 744  	bls.To.SetTarget(movw)
 745  
 746  	// BL runtime.morestack
 747  	call := obj.Appendp(movw, c.newprog)
 748  	call.As = obj.ACALL
 749  	call.To.Type = obj.TYPE_BRANCH
 750  	morestack := "runtime.morestack"
 751  	switch {
 752  	case c.cursym.CFunc():
 753  		morestack = "runtime.morestackc"
 754  	case !c.cursym.Func.Text.From.Sym.NeedCtxt():
 755  		morestack = "runtime.morestack_noctxt"
 756  	}
 757  	call.To.Sym = c.ctxt.Lookup(morestack)
 758  
 759  	pcdata = c.ctxt.EndUnsafePoint(call, c.newprog, -1)
 760  
 761  	// B start
 762  	b := obj.Appendp(pcdata, c.newprog)
 763  	b.As = obj.AJMP
 764  	b.To.Type = obj.TYPE_BRANCH
 765  	b.To.SetTarget(c.cursym.Func.Text.Link)
 766  	b.Spadj = +framesize
 767  
 768  	return end
 769  }
 770  
 771  var unaryDst = map[obj.As]bool{
 772  	ASWI:  true,
 773  	AWORD: true,
 774  }
 775  
 776  var Linkarm = obj.LinkArch{
 777  	Arch:           sys.ArchARM,
 778  	Init:           buildop,
 779  	Preprocess:     preprocess,
 780  	Assemble:       span5,
 781  	Progedit:       progedit,
 782  	UnaryDst:       unaryDst,
 783  	DWARFRegisters: ARMDWARFRegisters,
 784  }
 785