github.com/corona10/go@v0.0.0-20180224231303-7a218942be57/src/cmd/internal/obj/mips/obj0.go (about)

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