obj0.go raw

   1  // cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
   2  //
   3  //	Copyright © 1994-1999 Lucent Technologies Inc.  All rights reserved.
   4  //	Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
   5  //	Portions Copyright © 1997-1999 Vita Nuova Limited
   6  //	Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
   7  //	Portions Copyright © 2004,2006 Bruce Ellis
   8  //	Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
   9  //	Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
  10  //	Portions Copyright © 2009 The Go Authors. All rights reserved.
  11  //
  12  // Permission is hereby granted, free of charge, to any person obtaining a copy
  13  // of this software and associated documentation files (the "Software"), to deal
  14  // in the Software without restriction, including without limitation the rights
  15  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  16  // copies of the Software, and to permit persons to whom the Software is
  17  // furnished to do so, subject to the following conditions:
  18  //
  19  // The above copyright notice and this permission notice shall be included in
  20  // all copies or substantial portions of the Software.
  21  //
  22  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  23  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  24  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  25  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  26  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  27  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  28  // THE SOFTWARE.
  29  
  30  package mips
  31  
  32  import (
  33  	"github.com/twitchyliquid64/golang-asm/obj"
  34  	"github.com/twitchyliquid64/golang-asm/objabi"
  35  	"github.com/twitchyliquid64/golang-asm/sys"
  36  	"encoding/binary"
  37  	"fmt"
  38  	"math"
  39  )
  40  
  41  func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
  42  	c := ctxt0{ctxt: ctxt, newprog: newprog}
  43  
  44  	p.From.Class = 0
  45  	p.To.Class = 0
  46  
  47  	// Rewrite JMP/JAL to symbol as TYPE_BRANCH.
  48  	switch p.As {
  49  	case AJMP,
  50  		AJAL,
  51  		ARET,
  52  		obj.ADUFFZERO,
  53  		obj.ADUFFCOPY:
  54  		if p.To.Sym != nil {
  55  			p.To.Type = obj.TYPE_BRANCH
  56  		}
  57  	}
  58  
  59  	// Rewrite float constants to values stored in memory.
  60  	switch p.As {
  61  	case AMOVF:
  62  		if p.From.Type == obj.TYPE_FCONST {
  63  			f32 := float32(p.From.Val.(float64))
  64  			if math.Float32bits(f32) == 0 {
  65  				p.As = AMOVW
  66  				p.From.Type = obj.TYPE_REG
  67  				p.From.Reg = REGZERO
  68  				break
  69  			}
  70  			p.From.Type = obj.TYPE_MEM
  71  			p.From.Sym = ctxt.Float32Sym(f32)
  72  			p.From.Name = obj.NAME_EXTERN
  73  			p.From.Offset = 0
  74  		}
  75  
  76  	case AMOVD:
  77  		if p.From.Type == obj.TYPE_FCONST {
  78  			f64 := p.From.Val.(float64)
  79  			if math.Float64bits(f64) == 0 && c.ctxt.Arch.Family == sys.MIPS64 {
  80  				p.As = AMOVV
  81  				p.From.Type = obj.TYPE_REG
  82  				p.From.Reg = REGZERO
  83  				break
  84  			}
  85  			p.From.Type = obj.TYPE_MEM
  86  			p.From.Sym = ctxt.Float64Sym(f64)
  87  			p.From.Name = obj.NAME_EXTERN
  88  			p.From.Offset = 0
  89  		}
  90  
  91  		// Put >32-bit constants in memory and load them
  92  	case AMOVV:
  93  		if p.From.Type == obj.TYPE_CONST && p.From.Name == obj.NAME_NONE && p.From.Reg == 0 && int64(int32(p.From.Offset)) != p.From.Offset {
  94  			p.From.Type = obj.TYPE_MEM
  95  			p.From.Sym = ctxt.Int64Sym(p.From.Offset)
  96  			p.From.Name = obj.NAME_EXTERN
  97  			p.From.Offset = 0
  98  		}
  99  	}
 100  
 101  	// Rewrite SUB constants into ADD.
 102  	switch p.As {
 103  	case ASUB:
 104  		if p.From.Type == obj.TYPE_CONST {
 105  			p.From.Offset = -p.From.Offset
 106  			p.As = AADD
 107  		}
 108  
 109  	case ASUBU:
 110  		if p.From.Type == obj.TYPE_CONST {
 111  			p.From.Offset = -p.From.Offset
 112  			p.As = AADDU
 113  		}
 114  
 115  	case ASUBV:
 116  		if p.From.Type == obj.TYPE_CONST {
 117  			p.From.Offset = -p.From.Offset
 118  			p.As = AADDV
 119  		}
 120  
 121  	case ASUBVU:
 122  		if p.From.Type == obj.TYPE_CONST {
 123  			p.From.Offset = -p.From.Offset
 124  			p.As = AADDVU
 125  		}
 126  	}
 127  }
 128  
 129  func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
 130  	// TODO(minux): add morestack short-cuts with small fixed frame-size.
 131  	c := ctxt0{ctxt: ctxt, newprog: newprog, cursym: cursym}
 132  
 133  	// a switch for enabling/disabling instruction scheduling
 134  	nosched := true
 135  
 136  	if c.cursym.Func.Text == nil || c.cursym.Func.Text.Link == nil {
 137  		return
 138  	}
 139  
 140  	p := c.cursym.Func.Text
 141  	textstksiz := p.To.Offset
 142  	if textstksiz == -ctxt.FixedFrameSize() {
 143  		// Historical way to mark NOFRAME.
 144  		p.From.Sym.Set(obj.AttrNoFrame, true)
 145  		textstksiz = 0
 146  	}
 147  	if textstksiz < 0 {
 148  		c.ctxt.Diag("negative frame size %d - did you mean NOFRAME?", textstksiz)
 149  	}
 150  	if p.From.Sym.NoFrame() {
 151  		if textstksiz != 0 {
 152  			c.ctxt.Diag("NOFRAME functions must have a frame size of 0, not %d", textstksiz)
 153  		}
 154  	}
 155  
 156  	c.cursym.Func.Args = p.To.Val.(int32)
 157  	c.cursym.Func.Locals = int32(textstksiz)
 158  
 159  	/*
 160  	 * find leaf subroutines
 161  	 * expand RET
 162  	 * expand BECOME pseudo
 163  	 */
 164  
 165  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
 166  		switch p.As {
 167  		/* too hard, just leave alone */
 168  		case obj.ATEXT:
 169  			p.Mark |= LABEL | LEAF | SYNC
 170  			if p.Link != nil {
 171  				p.Link.Mark |= LABEL
 172  			}
 173  
 174  		/* too hard, just leave alone */
 175  		case AMOVW,
 176  			AMOVV:
 177  			if p.To.Type == obj.TYPE_REG && p.To.Reg >= REG_SPECIAL {
 178  				p.Mark |= LABEL | SYNC
 179  				break
 180  			}
 181  			if p.From.Type == obj.TYPE_REG && p.From.Reg >= REG_SPECIAL {
 182  				p.Mark |= LABEL | SYNC
 183  			}
 184  
 185  		/* too hard, just leave alone */
 186  		case ASYSCALL,
 187  			AWORD,
 188  			ATLBWR,
 189  			ATLBWI,
 190  			ATLBP,
 191  			ATLBR:
 192  			p.Mark |= LABEL | SYNC
 193  
 194  		case ANOR:
 195  			if p.To.Type == obj.TYPE_REG {
 196  				if p.To.Reg == REGZERO {
 197  					p.Mark |= LABEL | SYNC
 198  				}
 199  			}
 200  
 201  		case ABGEZAL,
 202  			ABLTZAL,
 203  			AJAL,
 204  			obj.ADUFFZERO,
 205  			obj.ADUFFCOPY:
 206  			c.cursym.Func.Text.Mark &^= LEAF
 207  			fallthrough
 208  
 209  		case AJMP,
 210  			ABEQ,
 211  			ABGEZ,
 212  			ABGTZ,
 213  			ABLEZ,
 214  			ABLTZ,
 215  			ABNE,
 216  			ABFPT, ABFPF:
 217  			if p.As == ABFPT || p.As == ABFPF {
 218  				// We don't treat ABFPT and ABFPF as branches here,
 219  				// so that we will always fill nop (0x0) in their
 220  				// delay slot during assembly.
 221  				// This is to workaround a kernel FPU emulator bug
 222  				// where it uses the user stack to simulate the
 223  				// instruction in the delay slot if it's not 0x0,
 224  				// and somehow that leads to SIGSEGV when the kernel
 225  				// jump to the stack.
 226  				p.Mark |= SYNC
 227  			} else {
 228  				p.Mark |= BRANCH
 229  			}
 230  			q1 := p.To.Target()
 231  			if q1 != nil {
 232  				for q1.As == obj.ANOP {
 233  					q1 = q1.Link
 234  					p.To.SetTarget(q1)
 235  				}
 236  
 237  				if q1.Mark&LEAF == 0 {
 238  					q1.Mark |= LABEL
 239  				}
 240  			}
 241  			//else {
 242  			//	p.Mark |= LABEL
 243  			//}
 244  			q1 = p.Link
 245  			if q1 != nil {
 246  				q1.Mark |= LABEL
 247  			}
 248  
 249  		case ARET:
 250  			if p.Link != nil {
 251  				p.Link.Mark |= LABEL
 252  			}
 253  		}
 254  	}
 255  
 256  	var mov, add obj.As
 257  	if c.ctxt.Arch.Family == sys.MIPS64 {
 258  		add = AADDV
 259  		mov = AMOVV
 260  	} else {
 261  		add = AADDU
 262  		mov = AMOVW
 263  	}
 264  
 265  	var q *obj.Prog
 266  	var q1 *obj.Prog
 267  	autosize := int32(0)
 268  	var p1 *obj.Prog
 269  	var p2 *obj.Prog
 270  	for p := c.cursym.Func.Text; p != nil; p = p.Link {
 271  		o := p.As
 272  		switch o {
 273  		case obj.ATEXT:
 274  			autosize = int32(textstksiz)
 275  
 276  			if p.Mark&LEAF != 0 && autosize == 0 {
 277  				// A leaf function with no locals has no frame.
 278  				p.From.Sym.Set(obj.AttrNoFrame, true)
 279  			}
 280  
 281  			if !p.From.Sym.NoFrame() {
 282  				// If there is a stack frame at all, it includes
 283  				// space to save the LR.
 284  				autosize += int32(c.ctxt.FixedFrameSize())
 285  			}
 286  
 287  			if autosize&4 != 0 && c.ctxt.Arch.Family == sys.MIPS64 {
 288  				autosize += 4
 289  			}
 290  
 291  			if autosize == 0 && c.cursym.Func.Text.Mark&LEAF == 0 {
 292  				if c.cursym.Func.Text.From.Sym.NoSplit() {
 293  					if ctxt.Debugvlog {
 294  						ctxt.Logf("save suppressed in: %s\n", c.cursym.Name)
 295  					}
 296  
 297  					c.cursym.Func.Text.Mark |= LEAF
 298  				}
 299  			}
 300  
 301  			p.To.Offset = int64(autosize) - ctxt.FixedFrameSize()
 302  
 303  			if c.cursym.Func.Text.Mark&LEAF != 0 {
 304  				c.cursym.Set(obj.AttrLeaf, true)
 305  				if p.From.Sym.NoFrame() {
 306  					break
 307  				}
 308  			}
 309  
 310  			if !p.From.Sym.NoSplit() {
 311  				p = c.stacksplit(p, autosize) // emit split check
 312  			}
 313  
 314  			q = p
 315  
 316  			if autosize != 0 {
 317  				// Make sure to save link register for non-empty frame, even if
 318  				// it is a leaf function, so that traceback works.
 319  				// Store link register before decrement SP, so if a signal comes
 320  				// during the execution of the function prologue, the traceback
 321  				// code will not see a half-updated stack frame.
 322  				// This sequence is not async preemptible, as if we open a frame
 323  				// at the current SP, it will clobber the saved LR.
 324  				q = c.ctxt.StartUnsafePoint(q, c.newprog)
 325  
 326  				q = obj.Appendp(q, newprog)
 327  				q.As = mov
 328  				q.Pos = p.Pos
 329  				q.From.Type = obj.TYPE_REG
 330  				q.From.Reg = REGLINK
 331  				q.To.Type = obj.TYPE_MEM
 332  				q.To.Offset = int64(-autosize)
 333  				q.To.Reg = REGSP
 334  
 335  				q = obj.Appendp(q, newprog)
 336  				q.As = add
 337  				q.Pos = p.Pos
 338  				q.From.Type = obj.TYPE_CONST
 339  				q.From.Offset = int64(-autosize)
 340  				q.To.Type = obj.TYPE_REG
 341  				q.To.Reg = REGSP
 342  				q.Spadj = +autosize
 343  
 344  				q = c.ctxt.EndUnsafePoint(q, c.newprog, -1)
 345  			}
 346  
 347  			if c.cursym.Func.Text.From.Sym.Wrapper() && c.cursym.Func.Text.Mark&LEAF == 0 {
 348  				// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
 349  				//
 350  				//	MOV	g_panic(g), R1
 351  				//	BEQ	R1, end
 352  				//	MOV	panic_argp(R1), R2
 353  				//	ADD	$(autosize+FIXED_FRAME), R29, R3
 354  				//	BNE	R2, R3, end
 355  				//	ADD	$FIXED_FRAME, R29, R2
 356  				//	MOV	R2, panic_argp(R1)
 357  				// end:
 358  				//	NOP
 359  				//
 360  				// The NOP is needed to give the jumps somewhere to land.
 361  				// It is a liblink NOP, not an mips NOP: it encodes to 0 instruction bytes.
 362  				//
 363  				// We don't generate this for leafs because that means the wrapped
 364  				// function was inlined into the wrapper.
 365  
 366  				q = obj.Appendp(q, newprog)
 367  
 368  				q.As = mov
 369  				q.From.Type = obj.TYPE_MEM
 370  				q.From.Reg = REGG
 371  				q.From.Offset = 4 * int64(c.ctxt.Arch.PtrSize) // G.panic
 372  				q.To.Type = obj.TYPE_REG
 373  				q.To.Reg = REG_R1
 374  
 375  				q = obj.Appendp(q, newprog)
 376  				q.As = ABEQ
 377  				q.From.Type = obj.TYPE_REG
 378  				q.From.Reg = REG_R1
 379  				q.To.Type = obj.TYPE_BRANCH
 380  				q.Mark |= BRANCH
 381  				p1 = q
 382  
 383  				q = obj.Appendp(q, newprog)
 384  				q.As = mov
 385  				q.From.Type = obj.TYPE_MEM
 386  				q.From.Reg = REG_R1
 387  				q.From.Offset = 0 // Panic.argp
 388  				q.To.Type = obj.TYPE_REG
 389  				q.To.Reg = REG_R2
 390  
 391  				q = obj.Appendp(q, newprog)
 392  				q.As = add
 393  				q.From.Type = obj.TYPE_CONST
 394  				q.From.Offset = int64(autosize) + ctxt.FixedFrameSize()
 395  				q.Reg = REGSP
 396  				q.To.Type = obj.TYPE_REG
 397  				q.To.Reg = REG_R3
 398  
 399  				q = obj.Appendp(q, newprog)
 400  				q.As = ABNE
 401  				q.From.Type = obj.TYPE_REG
 402  				q.From.Reg = REG_R2
 403  				q.Reg = REG_R3
 404  				q.To.Type = obj.TYPE_BRANCH
 405  				q.Mark |= BRANCH
 406  				p2 = q
 407  
 408  				q = obj.Appendp(q, newprog)
 409  				q.As = add
 410  				q.From.Type = obj.TYPE_CONST
 411  				q.From.Offset = ctxt.FixedFrameSize()
 412  				q.Reg = REGSP
 413  				q.To.Type = obj.TYPE_REG
 414  				q.To.Reg = REG_R2
 415  
 416  				q = obj.Appendp(q, newprog)
 417  				q.As = mov
 418  				q.From.Type = obj.TYPE_REG
 419  				q.From.Reg = REG_R2
 420  				q.To.Type = obj.TYPE_MEM
 421  				q.To.Reg = REG_R1
 422  				q.To.Offset = 0 // Panic.argp
 423  
 424  				q = obj.Appendp(q, newprog)
 425  
 426  				q.As = obj.ANOP
 427  				p1.To.SetTarget(q)
 428  				p2.To.SetTarget(q)
 429  			}
 430  
 431  		case ARET:
 432  			if p.From.Type == obj.TYPE_CONST {
 433  				ctxt.Diag("using BECOME (%v) is not supported!", p)
 434  				break
 435  			}
 436  
 437  			retSym := p.To.Sym
 438  			p.To.Name = obj.NAME_NONE // clear fields as we may modify p to other instruction
 439  			p.To.Sym = nil
 440  
 441  			if c.cursym.Func.Text.Mark&LEAF != 0 {
 442  				if autosize == 0 {
 443  					p.As = AJMP
 444  					p.From = obj.Addr{}
 445  					if retSym != nil { // retjmp
 446  						p.To.Type = obj.TYPE_BRANCH
 447  						p.To.Name = obj.NAME_EXTERN
 448  						p.To.Sym = retSym
 449  					} else {
 450  						p.To.Type = obj.TYPE_MEM
 451  						p.To.Reg = REGLINK
 452  						p.To.Offset = 0
 453  					}
 454  					p.Mark |= BRANCH
 455  					break
 456  				}
 457  
 458  				p.As = add
 459  				p.From.Type = obj.TYPE_CONST
 460  				p.From.Offset = int64(autosize)
 461  				p.To.Type = obj.TYPE_REG
 462  				p.To.Reg = REGSP
 463  				p.Spadj = -autosize
 464  
 465  				q = c.newprog()
 466  				q.As = AJMP
 467  				q.Pos = p.Pos
 468  				q.To.Type = obj.TYPE_MEM
 469  				q.To.Offset = 0
 470  				q.To.Reg = REGLINK
 471  				q.Mark |= BRANCH
 472  				q.Spadj = +autosize
 473  
 474  				q.Link = p.Link
 475  				p.Link = q
 476  				break
 477  			}
 478  
 479  			p.As = mov
 480  			p.From.Type = obj.TYPE_MEM
 481  			p.From.Offset = 0
 482  			p.From.Reg = REGSP
 483  			p.To.Type = obj.TYPE_REG
 484  			p.To.Reg = REGLINK
 485  
 486  			if autosize != 0 {
 487  				q = c.newprog()
 488  				q.As = add
 489  				q.Pos = p.Pos
 490  				q.From.Type = obj.TYPE_CONST
 491  				q.From.Offset = int64(autosize)
 492  				q.To.Type = obj.TYPE_REG
 493  				q.To.Reg = REGSP
 494  				q.Spadj = -autosize
 495  
 496  				q.Link = p.Link
 497  				p.Link = q
 498  			}
 499  
 500  			q1 = c.newprog()
 501  			q1.As = AJMP
 502  			q1.Pos = p.Pos
 503  			if retSym != nil { // retjmp
 504  				q1.To.Type = obj.TYPE_BRANCH
 505  				q1.To.Name = obj.NAME_EXTERN
 506  				q1.To.Sym = retSym
 507  			} else {
 508  				q1.To.Type = obj.TYPE_MEM
 509  				q1.To.Offset = 0
 510  				q1.To.Reg = REGLINK
 511  			}
 512  			q1.Mark |= BRANCH
 513  			q1.Spadj = +autosize
 514  
 515  			q1.Link = q.Link
 516  			q.Link = q1
 517  
 518  		case AADD,
 519  			AADDU,
 520  			AADDV,
 521  			AADDVU:
 522  			if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.From.Type == obj.TYPE_CONST {
 523  				p.Spadj = int32(-p.From.Offset)
 524  			}
 525  
 526  		case obj.AGETCALLERPC:
 527  			if cursym.Leaf() {
 528  				/* MOV LR, Rd */
 529  				p.As = mov
 530  				p.From.Type = obj.TYPE_REG
 531  				p.From.Reg = REGLINK
 532  			} else {
 533  				/* MOV (RSP), Rd */
 534  				p.As = mov
 535  				p.From.Type = obj.TYPE_MEM
 536  				p.From.Reg = REGSP
 537  			}
 538  		}
 539  	}
 540  
 541  	if c.ctxt.Arch.Family == sys.MIPS {
 542  		// rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
 543  		for p = c.cursym.Func.Text; p != nil; p = p1 {
 544  			p1 = p.Link
 545  
 546  			if p.As != AMOVD {
 547  				continue
 548  			}
 549  			if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
 550  				continue
 551  			}
 552  
 553  			p.As = AMOVF
 554  			q = c.newprog()
 555  			*q = *p
 556  			q.Link = p.Link
 557  			p.Link = q
 558  			p1 = q.Link
 559  
 560  			var addrOff int64
 561  			if c.ctxt.Arch.ByteOrder == binary.BigEndian {
 562  				addrOff = 4 // swap load/save order
 563  			}
 564  			if p.From.Type == obj.TYPE_MEM {
 565  				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
 566  				p.To.Reg = reg
 567  				q.To.Reg = reg + 1
 568  				p.From.Offset += addrOff
 569  				q.From.Offset += 4 - addrOff
 570  			} else if p.To.Type == obj.TYPE_MEM {
 571  				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
 572  				p.From.Reg = reg
 573  				q.From.Reg = reg + 1
 574  				p.To.Offset += addrOff
 575  				q.To.Offset += 4 - addrOff
 576  			}
 577  		}
 578  	}
 579  
 580  	if nosched {
 581  		// if we don't do instruction scheduling, simply add
 582  		// NOP after each branch instruction.
 583  		for p = c.cursym.Func.Text; p != nil; p = p.Link {
 584  			if p.Mark&BRANCH != 0 {
 585  				c.addnop(p)
 586  			}
 587  		}
 588  		return
 589  	}
 590  
 591  	// instruction scheduling
 592  	q = nil                 // p - 1
 593  	q1 = c.cursym.Func.Text // top of block
 594  	o := 0                  // count of instructions
 595  	for p = c.cursym.Func.Text; p != nil; p = p1 {
 596  		p1 = p.Link
 597  		o++
 598  		if p.Mark&NOSCHED != 0 {
 599  			if q1 != p {
 600  				c.sched(q1, q)
 601  			}
 602  			for ; p != nil; p = p.Link {
 603  				if p.Mark&NOSCHED == 0 {
 604  					break
 605  				}
 606  				q = p
 607  			}
 608  			p1 = p
 609  			q1 = p
 610  			o = 0
 611  			continue
 612  		}
 613  		if p.Mark&(LABEL|SYNC) != 0 {
 614  			if q1 != p {
 615  				c.sched(q1, q)
 616  			}
 617  			q1 = p
 618  			o = 1
 619  		}
 620  		if p.Mark&(BRANCH|SYNC) != 0 {
 621  			c.sched(q1, p)
 622  			q1 = p1
 623  			o = 0
 624  		}
 625  		if o >= NSCHED {
 626  			c.sched(q1, p)
 627  			q1 = p1
 628  			o = 0
 629  		}
 630  		q = p
 631  	}
 632  }
 633  
 634  func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
 635  	var mov, add, sub obj.As
 636  
 637  	if c.ctxt.Arch.Family == sys.MIPS64 {
 638  		add = AADDV
 639  		mov = AMOVV
 640  		sub = ASUBVU
 641  	} else {
 642  		add = AADDU
 643  		mov = AMOVW
 644  		sub = ASUBU
 645  	}
 646  
 647  	// MOV	g_stackguard(g), R1
 648  	p = obj.Appendp(p, c.newprog)
 649  
 650  	p.As = mov
 651  	p.From.Type = obj.TYPE_MEM
 652  	p.From.Reg = REGG
 653  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
 654  	if c.cursym.CFunc() {
 655  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
 656  	}
 657  	p.To.Type = obj.TYPE_REG
 658  	p.To.Reg = REG_R1
 659  
 660  	// Mark the stack bound check and morestack call async nonpreemptible.
 661  	// If we get preempted here, when resumed the preemption request is
 662  	// cleared, but we'll still call morestack, which will double the stack
 663  	// unnecessarily. See issue #35470.
 664  	p = c.ctxt.StartUnsafePoint(p, c.newprog)
 665  
 666  	var q *obj.Prog
 667  	if framesize <= objabi.StackSmall {
 668  		// small stack: SP < stackguard
 669  		//	AGTU	SP, stackguard, R1
 670  		p = obj.Appendp(p, c.newprog)
 671  
 672  		p.As = ASGTU
 673  		p.From.Type = obj.TYPE_REG
 674  		p.From.Reg = REGSP
 675  		p.Reg = REG_R1
 676  		p.To.Type = obj.TYPE_REG
 677  		p.To.Reg = REG_R1
 678  	} else if framesize <= objabi.StackBig {
 679  		// large stack: SP-framesize < stackguard-StackSmall
 680  		//	ADD	$-(framesize-StackSmall), SP, R2
 681  		//	SGTU	R2, stackguard, R1
 682  		p = obj.Appendp(p, c.newprog)
 683  
 684  		p.As = add
 685  		p.From.Type = obj.TYPE_CONST
 686  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
 687  		p.Reg = REGSP
 688  		p.To.Type = obj.TYPE_REG
 689  		p.To.Reg = REG_R2
 690  
 691  		p = obj.Appendp(p, c.newprog)
 692  		p.As = ASGTU
 693  		p.From.Type = obj.TYPE_REG
 694  		p.From.Reg = REG_R2
 695  		p.Reg = REG_R1
 696  		p.To.Type = obj.TYPE_REG
 697  		p.To.Reg = REG_R1
 698  	} else {
 699  		// Such a large stack we need to protect against wraparound.
 700  		// If SP is close to zero:
 701  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
 702  		// The +StackGuard on both sides is required to keep the left side positive:
 703  		// SP is allowed to be slightly below stackguard. See stack.h.
 704  		//
 705  		// Preemption sets stackguard to StackPreempt, a very large value.
 706  		// That breaks the math above, so we have to check for that explicitly.
 707  		//	// stackguard is R1
 708  		//	MOV	$StackPreempt, R2
 709  		//	BEQ	R1, R2, label-of-call-to-morestack
 710  		//	ADD	$StackGuard, SP, R2
 711  		//	SUB	R1, R2
 712  		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
 713  		//	SGTU	R2, R1, R1
 714  		p = obj.Appendp(p, c.newprog)
 715  
 716  		p.As = mov
 717  		p.From.Type = obj.TYPE_CONST
 718  		p.From.Offset = objabi.StackPreempt
 719  		p.To.Type = obj.TYPE_REG
 720  		p.To.Reg = REG_R2
 721  
 722  		p = obj.Appendp(p, c.newprog)
 723  		q = p
 724  		p.As = ABEQ
 725  		p.From.Type = obj.TYPE_REG
 726  		p.From.Reg = REG_R1
 727  		p.Reg = REG_R2
 728  		p.To.Type = obj.TYPE_BRANCH
 729  		p.Mark |= BRANCH
 730  
 731  		p = obj.Appendp(p, c.newprog)
 732  		p.As = add
 733  		p.From.Type = obj.TYPE_CONST
 734  		p.From.Offset = int64(objabi.StackGuard)
 735  		p.Reg = REGSP
 736  		p.To.Type = obj.TYPE_REG
 737  		p.To.Reg = REG_R2
 738  
 739  		p = obj.Appendp(p, c.newprog)
 740  		p.As = sub
 741  		p.From.Type = obj.TYPE_REG
 742  		p.From.Reg = REG_R1
 743  		p.To.Type = obj.TYPE_REG
 744  		p.To.Reg = REG_R2
 745  
 746  		p = obj.Appendp(p, c.newprog)
 747  		p.As = mov
 748  		p.From.Type = obj.TYPE_CONST
 749  		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
 750  		p.To.Type = obj.TYPE_REG
 751  		p.To.Reg = REG_R1
 752  
 753  		p = obj.Appendp(p, c.newprog)
 754  		p.As = ASGTU
 755  		p.From.Type = obj.TYPE_REG
 756  		p.From.Reg = REG_R2
 757  		p.Reg = REG_R1
 758  		p.To.Type = obj.TYPE_REG
 759  		p.To.Reg = REG_R1
 760  	}
 761  
 762  	// q1: BNE	R1, done
 763  	p = obj.Appendp(p, c.newprog)
 764  	q1 := p
 765  
 766  	p.As = ABNE
 767  	p.From.Type = obj.TYPE_REG
 768  	p.From.Reg = REG_R1
 769  	p.To.Type = obj.TYPE_BRANCH
 770  	p.Mark |= BRANCH
 771  
 772  	// MOV	LINK, R3
 773  	p = obj.Appendp(p, c.newprog)
 774  
 775  	p.As = mov
 776  	p.From.Type = obj.TYPE_REG
 777  	p.From.Reg = REGLINK
 778  	p.To.Type = obj.TYPE_REG
 779  	p.To.Reg = REG_R3
 780  	if q != nil {
 781  		q.To.SetTarget(p)
 782  		p.Mark |= LABEL
 783  	}
 784  
 785  	p = c.ctxt.EmitEntryStackMap(c.cursym, p, c.newprog)
 786  
 787  	// JAL	runtime.morestack(SB)
 788  	p = obj.Appendp(p, c.newprog)
 789  
 790  	p.As = AJAL
 791  	p.To.Type = obj.TYPE_BRANCH
 792  	if c.cursym.CFunc() {
 793  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
 794  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
 795  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
 796  	} else {
 797  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
 798  	}
 799  	p.Mark |= BRANCH
 800  
 801  	p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
 802  
 803  	// JMP	start
 804  	p = obj.Appendp(p, c.newprog)
 805  
 806  	p.As = AJMP
 807  	p.To.Type = obj.TYPE_BRANCH
 808  	p.To.SetTarget(c.cursym.Func.Text.Link)
 809  	p.Mark |= BRANCH
 810  
 811  	// placeholder for q1's jump target
 812  	p = obj.Appendp(p, c.newprog)
 813  
 814  	p.As = obj.ANOP // zero-width place holder
 815  	q1.To.SetTarget(p)
 816  
 817  	return p
 818  }
 819  
 820  func (c *ctxt0) addnop(p *obj.Prog) {
 821  	q := c.newprog()
 822  	q.As = ANOOP
 823  	q.Pos = p.Pos
 824  	q.Link = p.Link
 825  	p.Link = q
 826  }
 827  
 828  const (
 829  	E_HILO  = 1 << 0
 830  	E_FCR   = 1 << 1
 831  	E_MCR   = 1 << 2
 832  	E_MEM   = 1 << 3
 833  	E_MEMSP = 1 << 4 /* uses offset and size */
 834  	E_MEMSB = 1 << 5 /* uses offset and size */
 835  	ANYMEM  = E_MEM | E_MEMSP | E_MEMSB
 836  	//DELAY = LOAD|BRANCH|FCMP
 837  	DELAY = BRANCH /* only schedule branch */
 838  )
 839  
 840  type Dep struct {
 841  	ireg uint32
 842  	freg uint32
 843  	cc   uint32
 844  }
 845  
 846  type Sch struct {
 847  	p       obj.Prog
 848  	set     Dep
 849  	used    Dep
 850  	soffset int32
 851  	size    uint8
 852  	nop     uint8
 853  	comp    bool
 854  }
 855  
 856  func (c *ctxt0) sched(p0, pe *obj.Prog) {
 857  	var sch [NSCHED]Sch
 858  
 859  	/*
 860  	 * build side structure
 861  	 */
 862  	s := sch[:]
 863  	for p := p0; ; p = p.Link {
 864  		s[0].p = *p
 865  		c.markregused(&s[0])
 866  		if p == pe {
 867  			break
 868  		}
 869  		s = s[1:]
 870  	}
 871  	se := s
 872  
 873  	for i := cap(sch) - cap(se); i >= 0; i-- {
 874  		s = sch[i:]
 875  		if s[0].p.Mark&DELAY == 0 {
 876  			continue
 877  		}
 878  		if -cap(s) < -cap(se) {
 879  			if !conflict(&s[0], &s[1]) {
 880  				continue
 881  			}
 882  		}
 883  
 884  		var t []Sch
 885  		var j int
 886  		for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
 887  			t = sch[j:]
 888  			if t[0].comp {
 889  				if s[0].p.Mark&BRANCH != 0 {
 890  					continue
 891  				}
 892  			}
 893  			if t[0].p.Mark&DELAY != 0 {
 894  				if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
 895  					continue
 896  				}
 897  			}
 898  			for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
 899  				if c.depend(&u[0], &t[0]) {
 900  					continue
 901  				}
 902  			}
 903  			goto out2
 904  		}
 905  
 906  		if s[0].p.Mark&BRANCH != 0 {
 907  			s[0].nop = 1
 908  		}
 909  		continue
 910  
 911  	out2:
 912  		// t[0] is the instruction being moved to fill the delay
 913  		stmp := t[0]
 914  		copy(t[:i-j], t[1:i-j+1])
 915  		s[0] = stmp
 916  
 917  		if t[i-j-1].p.Mark&BRANCH != 0 {
 918  			// t[i-j] is being put into a branch delay slot
 919  			// combine its Spadj with the branch instruction
 920  			t[i-j-1].p.Spadj += t[i-j].p.Spadj
 921  			t[i-j].p.Spadj = 0
 922  		}
 923  
 924  		i--
 925  	}
 926  
 927  	/*
 928  	 * put it all back
 929  	 */
 930  	var p *obj.Prog
 931  	var q *obj.Prog
 932  	for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
 933  		q = p.Link
 934  		if q != s[0].p.Link {
 935  			*p = s[0].p
 936  			p.Link = q
 937  		}
 938  		for s[0].nop != 0 {
 939  			s[0].nop--
 940  			c.addnop(p)
 941  		}
 942  	}
 943  }
 944  
 945  func (c *ctxt0) markregused(s *Sch) {
 946  	p := &s.p
 947  	s.comp = c.compound(p)
 948  	s.nop = 0
 949  	if s.comp {
 950  		s.set.ireg |= 1 << (REGTMP - REG_R0)
 951  		s.used.ireg |= 1 << (REGTMP - REG_R0)
 952  	}
 953  
 954  	ar := 0  /* dest is really reference */
 955  	ad := 0  /* source/dest is really address */
 956  	ld := 0  /* opcode is load instruction */
 957  	sz := 20 /* size of load/store for overlap computation */
 958  
 959  	/*
 960  	 * flags based on opcode
 961  	 */
 962  	switch p.As {
 963  	case obj.ATEXT:
 964  		c.autosize = int32(p.To.Offset + 8)
 965  		ad = 1
 966  
 967  	case AJAL:
 968  		r := p.Reg
 969  		if r == 0 {
 970  			r = REGLINK
 971  		}
 972  		s.set.ireg |= 1 << uint(r-REG_R0)
 973  		ar = 1
 974  		ad = 1
 975  
 976  	case ABGEZAL,
 977  		ABLTZAL:
 978  		s.set.ireg |= 1 << (REGLINK - REG_R0)
 979  		fallthrough
 980  	case ABEQ,
 981  		ABGEZ,
 982  		ABGTZ,
 983  		ABLEZ,
 984  		ABLTZ,
 985  		ABNE:
 986  		ar = 1
 987  		ad = 1
 988  
 989  	case ABFPT,
 990  		ABFPF:
 991  		ad = 1
 992  		s.used.cc |= E_FCR
 993  
 994  	case ACMPEQD,
 995  		ACMPEQF,
 996  		ACMPGED,
 997  		ACMPGEF,
 998  		ACMPGTD,
 999  		ACMPGTF:
1000  		ar = 1
1001  		s.set.cc |= E_FCR
1002  		p.Mark |= FCMP
1003  
1004  	case AJMP:
1005  		ar = 1
1006  		ad = 1
1007  
1008  	case AMOVB,
1009  		AMOVBU:
1010  		sz = 1
1011  		ld = 1
1012  
1013  	case AMOVH,
1014  		AMOVHU:
1015  		sz = 2
1016  		ld = 1
1017  
1018  	case AMOVF,
1019  		AMOVW,
1020  		AMOVWL,
1021  		AMOVWR:
1022  		sz = 4
1023  		ld = 1
1024  
1025  	case AMOVD,
1026  		AMOVV,
1027  		AMOVVL,
1028  		AMOVVR:
1029  		sz = 8
1030  		ld = 1
1031  
1032  	case ADIV,
1033  		ADIVU,
1034  		AMUL,
1035  		AMULU,
1036  		AREM,
1037  		AREMU,
1038  		ADIVV,
1039  		ADIVVU,
1040  		AMULV,
1041  		AMULVU,
1042  		AREMV,
1043  		AREMVU:
1044  		s.set.cc = E_HILO
1045  		fallthrough
1046  	case AADD,
1047  		AADDU,
1048  		AADDV,
1049  		AADDVU,
1050  		AAND,
1051  		ANOR,
1052  		AOR,
1053  		ASGT,
1054  		ASGTU,
1055  		ASLL,
1056  		ASRA,
1057  		ASRL,
1058  		ASLLV,
1059  		ASRAV,
1060  		ASRLV,
1061  		ASUB,
1062  		ASUBU,
1063  		ASUBV,
1064  		ASUBVU,
1065  		AXOR,
1066  
1067  		AADDD,
1068  		AADDF,
1069  		AADDW,
1070  		ASUBD,
1071  		ASUBF,
1072  		ASUBW,
1073  		AMULF,
1074  		AMULD,
1075  		AMULW,
1076  		ADIVF,
1077  		ADIVD,
1078  		ADIVW:
1079  		if p.Reg == 0 {
1080  			if p.To.Type == obj.TYPE_REG {
1081  				p.Reg = p.To.Reg
1082  			}
1083  			//if(p->reg == NREG)
1084  			//	print("botch %P\n", p);
1085  		}
1086  	}
1087  
1088  	/*
1089  	 * flags based on 'to' field
1090  	 */
1091  	cls := int(p.To.Class)
1092  	if cls == 0 {
1093  		cls = c.aclass(&p.To) + 1
1094  		p.To.Class = int8(cls)
1095  	}
1096  	cls--
1097  	switch cls {
1098  	default:
1099  		fmt.Printf("unknown class %d %v\n", cls, p)
1100  
1101  	case C_ZCON,
1102  		C_SCON,
1103  		C_ADD0CON,
1104  		C_AND0CON,
1105  		C_ADDCON,
1106  		C_ANDCON,
1107  		C_UCON,
1108  		C_LCON,
1109  		C_NONE,
1110  		C_SBRA,
1111  		C_LBRA,
1112  		C_ADDR,
1113  		C_TEXTSIZE:
1114  		break
1115  
1116  	case C_HI,
1117  		C_LO:
1118  		s.set.cc |= E_HILO
1119  
1120  	case C_FCREG:
1121  		s.set.cc |= E_FCR
1122  
1123  	case C_MREG:
1124  		s.set.cc |= E_MCR
1125  
1126  	case C_ZOREG,
1127  		C_SOREG,
1128  		C_LOREG:
1129  		cls = int(p.To.Reg)
1130  		s.used.ireg |= 1 << uint(cls-REG_R0)
1131  		if ad != 0 {
1132  			break
1133  		}
1134  		s.size = uint8(sz)
1135  		s.soffset = c.regoff(&p.To)
1136  
1137  		m := uint32(ANYMEM)
1138  		if cls == REGSB {
1139  			m = E_MEMSB
1140  		}
1141  		if cls == REGSP {
1142  			m = E_MEMSP
1143  		}
1144  
1145  		if ar != 0 {
1146  			s.used.cc |= m
1147  		} else {
1148  			s.set.cc |= m
1149  		}
1150  
1151  	case C_SACON,
1152  		C_LACON:
1153  		s.used.ireg |= 1 << (REGSP - REG_R0)
1154  
1155  	case C_SECON,
1156  		C_LECON:
1157  		s.used.ireg |= 1 << (REGSB - REG_R0)
1158  
1159  	case C_REG:
1160  		if ar != 0 {
1161  			s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
1162  		} else {
1163  			s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
1164  		}
1165  
1166  	case C_FREG:
1167  		if ar != 0 {
1168  			s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
1169  		} else {
1170  			s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
1171  		}
1172  		if ld != 0 && p.From.Type == obj.TYPE_REG {
1173  			p.Mark |= LOAD
1174  		}
1175  
1176  	case C_SAUTO,
1177  		C_LAUTO:
1178  		s.used.ireg |= 1 << (REGSP - REG_R0)
1179  		if ad != 0 {
1180  			break
1181  		}
1182  		s.size = uint8(sz)
1183  		s.soffset = c.regoff(&p.To)
1184  
1185  		if ar != 0 {
1186  			s.used.cc |= E_MEMSP
1187  		} else {
1188  			s.set.cc |= E_MEMSP
1189  		}
1190  
1191  	case C_SEXT,
1192  		C_LEXT:
1193  		s.used.ireg |= 1 << (REGSB - REG_R0)
1194  		if ad != 0 {
1195  			break
1196  		}
1197  		s.size = uint8(sz)
1198  		s.soffset = c.regoff(&p.To)
1199  
1200  		if ar != 0 {
1201  			s.used.cc |= E_MEMSB
1202  		} else {
1203  			s.set.cc |= E_MEMSB
1204  		}
1205  	}
1206  
1207  	/*
1208  	 * flags based on 'from' field
1209  	 */
1210  	cls = int(p.From.Class)
1211  	if cls == 0 {
1212  		cls = c.aclass(&p.From) + 1
1213  		p.From.Class = int8(cls)
1214  	}
1215  	cls--
1216  	switch cls {
1217  	default:
1218  		fmt.Printf("unknown class %d %v\n", cls, p)
1219  
1220  	case C_ZCON,
1221  		C_SCON,
1222  		C_ADD0CON,
1223  		C_AND0CON,
1224  		C_ADDCON,
1225  		C_ANDCON,
1226  		C_UCON,
1227  		C_LCON,
1228  		C_NONE,
1229  		C_SBRA,
1230  		C_LBRA,
1231  		C_ADDR,
1232  		C_TEXTSIZE:
1233  		break
1234  
1235  	case C_HI,
1236  		C_LO:
1237  		s.used.cc |= E_HILO
1238  
1239  	case C_FCREG:
1240  		s.used.cc |= E_FCR
1241  
1242  	case C_MREG:
1243  		s.used.cc |= E_MCR
1244  
1245  	case C_ZOREG,
1246  		C_SOREG,
1247  		C_LOREG:
1248  		cls = int(p.From.Reg)
1249  		s.used.ireg |= 1 << uint(cls-REG_R0)
1250  		if ld != 0 {
1251  			p.Mark |= LOAD
1252  		}
1253  		s.size = uint8(sz)
1254  		s.soffset = c.regoff(&p.From)
1255  
1256  		m := uint32(ANYMEM)
1257  		if cls == REGSB {
1258  			m = E_MEMSB
1259  		}
1260  		if cls == REGSP {
1261  			m = E_MEMSP
1262  		}
1263  
1264  		s.used.cc |= m
1265  
1266  	case C_SACON,
1267  		C_LACON:
1268  		cls = int(p.From.Reg)
1269  		if cls == 0 {
1270  			cls = REGSP
1271  		}
1272  		s.used.ireg |= 1 << uint(cls-REG_R0)
1273  
1274  	case C_SECON,
1275  		C_LECON:
1276  		s.used.ireg |= 1 << (REGSB - REG_R0)
1277  
1278  	case C_REG:
1279  		s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
1280  
1281  	case C_FREG:
1282  		s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
1283  		if ld != 0 && p.To.Type == obj.TYPE_REG {
1284  			p.Mark |= LOAD
1285  		}
1286  
1287  	case C_SAUTO,
1288  		C_LAUTO:
1289  		s.used.ireg |= 1 << (REGSP - REG_R0)
1290  		if ld != 0 {
1291  			p.Mark |= LOAD
1292  		}
1293  		if ad != 0 {
1294  			break
1295  		}
1296  		s.size = uint8(sz)
1297  		s.soffset = c.regoff(&p.From)
1298  
1299  		s.used.cc |= E_MEMSP
1300  
1301  	case C_SEXT:
1302  	case C_LEXT:
1303  		s.used.ireg |= 1 << (REGSB - REG_R0)
1304  		if ld != 0 {
1305  			p.Mark |= LOAD
1306  		}
1307  		if ad != 0 {
1308  			break
1309  		}
1310  		s.size = uint8(sz)
1311  		s.soffset = c.regoff(&p.From)
1312  
1313  		s.used.cc |= E_MEMSB
1314  	}
1315  
1316  	cls = int(p.Reg)
1317  	if cls != 0 {
1318  		if REG_F0 <= cls && cls <= REG_F31 {
1319  			s.used.freg |= 1 << uint(cls-REG_F0)
1320  		} else {
1321  			s.used.ireg |= 1 << uint(cls-REG_R0)
1322  		}
1323  	}
1324  	s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
1325  }
1326  
1327  /*
1328   * test to see if two instructions can be
1329   * interchanged without changing semantics
1330   */
1331  func (c *ctxt0) depend(sa, sb *Sch) bool {
1332  	if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
1333  		return true
1334  	}
1335  	if sb.set.ireg&sa.used.ireg != 0 {
1336  		return true
1337  	}
1338  
1339  	if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
1340  		return true
1341  	}
1342  	if sb.set.freg&sa.used.freg != 0 {
1343  		return true
1344  	}
1345  
1346  	/*
1347  	 * special case.
1348  	 * loads from same address cannot pass.
1349  	 * this is for hardware fifo's and the like
1350  	 */
1351  	if sa.used.cc&sb.used.cc&E_MEM != 0 {
1352  		if sa.p.Reg == sb.p.Reg {
1353  			if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) {
1354  				return true
1355  			}
1356  		}
1357  	}
1358  
1359  	x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
1360  	if x != 0 {
1361  		/*
1362  		 * allow SB and SP to pass each other.
1363  		 * allow SB to pass SB iff doffsets are ok
1364  		 * anything else conflicts
1365  		 */
1366  		if x != E_MEMSP && x != E_MEMSB {
1367  			return true
1368  		}
1369  		x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
1370  		if x&E_MEM != 0 {
1371  			return true
1372  		}
1373  		if offoverlap(sa, sb) {
1374  			return true
1375  		}
1376  	}
1377  
1378  	return false
1379  }
1380  
1381  func offoverlap(sa, sb *Sch) bool {
1382  	if sa.soffset < sb.soffset {
1383  		if sa.soffset+int32(sa.size) > sb.soffset {
1384  			return true
1385  		}
1386  		return false
1387  	}
1388  	if sb.soffset+int32(sb.size) > sa.soffset {
1389  		return true
1390  	}
1391  	return false
1392  }
1393  
1394  /*
1395   * test 2 adjacent instructions
1396   * and find out if inserted instructions
1397   * are desired to prevent stalls.
1398   */
1399  func conflict(sa, sb *Sch) bool {
1400  	if sa.set.ireg&sb.used.ireg != 0 {
1401  		return true
1402  	}
1403  	if sa.set.freg&sb.used.freg != 0 {
1404  		return true
1405  	}
1406  	if sa.set.cc&sb.used.cc != 0 {
1407  		return true
1408  	}
1409  	return false
1410  }
1411  
1412  func (c *ctxt0) compound(p *obj.Prog) bool {
1413  	o := c.oplook(p)
1414  	if o.size != 4 {
1415  		return true
1416  	}
1417  	if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
1418  		return true
1419  	}
1420  	return false
1421  }
1422  
1423  var Linkmips64 = obj.LinkArch{
1424  	Arch:           sys.ArchMIPS64,
1425  	Init:           buildop,
1426  	Preprocess:     preprocess,
1427  	Assemble:       span0,
1428  	Progedit:       progedit,
1429  	DWARFRegisters: MIPSDWARFRegisters,
1430  }
1431  
1432  var Linkmips64le = obj.LinkArch{
1433  	Arch:           sys.ArchMIPS64LE,
1434  	Init:           buildop,
1435  	Preprocess:     preprocess,
1436  	Assemble:       span0,
1437  	Progedit:       progedit,
1438  	DWARFRegisters: MIPSDWARFRegisters,
1439  }
1440  
1441  var Linkmips = obj.LinkArch{
1442  	Arch:           sys.ArchMIPS,
1443  	Init:           buildop,
1444  	Preprocess:     preprocess,
1445  	Assemble:       span0,
1446  	Progedit:       progedit,
1447  	DWARFRegisters: MIPSDWARFRegisters,
1448  }
1449  
1450  var Linkmipsle = obj.LinkArch{
1451  	Arch:           sys.ArchMIPSLE,
1452  	Init:           buildop,
1453  	Preprocess:     preprocess,
1454  	Assemble:       span0,
1455  	Progedit:       progedit,
1456  	DWARFRegisters: MIPSDWARFRegisters,
1457  }
1458