github.com/gernest/nezuko@v0.1.2/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  	"encoding/binary"
    34  	"fmt"
    35  	"github.com/gernest/nezuko/internal/obj"
    36  	"github.com/gernest/nezuko/internal/objabi"
    37  	"github.com/gernest/nezuko/internal/sys"
    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  		case obj.AGETCALLERPC:
   541  			if cursym.Leaf() {
   542  				/* MOV LR, Rd */
   543  				p.As = mov
   544  				p.From.Type = obj.TYPE_REG
   545  				p.From.Reg = REGLINK
   546  			} else {
   547  				/* MOV (RSP), Rd */
   548  				p.As = mov
   549  				p.From.Type = obj.TYPE_MEM
   550  				p.From.Reg = REGSP
   551  			}
   552  		}
   553  	}
   554  
   555  	if c.ctxt.Arch.Family == sys.MIPS {
   556  		// rewrite MOVD into two MOVF in 32-bit mode to avoid unaligned memory access
   557  		for p = c.cursym.Func.Text; p != nil; p = p1 {
   558  			p1 = p.Link
   559  
   560  			if p.As != AMOVD {
   561  				continue
   562  			}
   563  			if p.From.Type != obj.TYPE_MEM && p.To.Type != obj.TYPE_MEM {
   564  				continue
   565  			}
   566  
   567  			p.As = AMOVF
   568  			q = c.newprog()
   569  			*q = *p
   570  			q.Link = p.Link
   571  			p.Link = q
   572  			p1 = q.Link
   573  
   574  			var addrOff int64
   575  			if c.ctxt.Arch.ByteOrder == binary.BigEndian {
   576  				addrOff = 4 // swap load/save order
   577  			}
   578  			if p.From.Type == obj.TYPE_MEM {
   579  				reg := REG_F0 + (p.To.Reg-REG_F0)&^1
   580  				p.To.Reg = reg
   581  				q.To.Reg = reg + 1
   582  				p.From.Offset += addrOff
   583  				q.From.Offset += 4 - addrOff
   584  			} else if p.To.Type == obj.TYPE_MEM {
   585  				reg := REG_F0 + (p.From.Reg-REG_F0)&^1
   586  				p.From.Reg = reg
   587  				q.From.Reg = reg + 1
   588  				p.To.Offset += addrOff
   589  				q.To.Offset += 4 - addrOff
   590  			}
   591  		}
   592  	}
   593  
   594  	if nosched {
   595  		// if we don't do instruction scheduling, simply add
   596  		// NOP after each branch instruction.
   597  		for p = c.cursym.Func.Text; p != nil; p = p.Link {
   598  			if p.Mark&BRANCH != 0 {
   599  				c.addnop(p)
   600  			}
   601  		}
   602  		return
   603  	}
   604  
   605  	// instruction scheduling
   606  	q = nil                 // p - 1
   607  	q1 = c.cursym.Func.Text // top of block
   608  	o := 0                  // count of instructions
   609  	for p = c.cursym.Func.Text; p != nil; p = p1 {
   610  		p1 = p.Link
   611  		o++
   612  		if p.Mark&NOSCHED != 0 {
   613  			if q1 != p {
   614  				c.sched(q1, q)
   615  			}
   616  			for ; p != nil; p = p.Link {
   617  				if p.Mark&NOSCHED == 0 {
   618  					break
   619  				}
   620  				q = p
   621  			}
   622  			p1 = p
   623  			q1 = p
   624  			o = 0
   625  			continue
   626  		}
   627  		if p.Mark&(LABEL|SYNC) != 0 {
   628  			if q1 != p {
   629  				c.sched(q1, q)
   630  			}
   631  			q1 = p
   632  			o = 1
   633  		}
   634  		if p.Mark&(BRANCH|SYNC) != 0 {
   635  			c.sched(q1, p)
   636  			q1 = p1
   637  			o = 0
   638  		}
   639  		if o >= NSCHED {
   640  			c.sched(q1, p)
   641  			q1 = p1
   642  			o = 0
   643  		}
   644  		q = p
   645  	}
   646  }
   647  
   648  func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
   649  	var mov, add, sub obj.As
   650  
   651  	if c.ctxt.Arch.Family == sys.MIPS64 {
   652  		add = AADDV
   653  		mov = AMOVV
   654  		sub = ASUBVU
   655  	} else {
   656  		add = AADDU
   657  		mov = AMOVW
   658  		sub = ASUBU
   659  	}
   660  
   661  	// MOV	g_stackguard(g), R1
   662  	p = obj.Appendp(p, c.newprog)
   663  
   664  	p.As = mov
   665  	p.From.Type = obj.TYPE_MEM
   666  	p.From.Reg = REGG
   667  	p.From.Offset = 2 * int64(c.ctxt.Arch.PtrSize) // G.stackguard0
   668  	if c.cursym.CFunc() {
   669  		p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
   670  	}
   671  	p.To.Type = obj.TYPE_REG
   672  	p.To.Reg = REG_R1
   673  
   674  	var q *obj.Prog
   675  	if framesize <= objabi.StackSmall {
   676  		// small stack: SP < stackguard
   677  		//	AGTU	SP, stackguard, R1
   678  		p = obj.Appendp(p, c.newprog)
   679  
   680  		p.As = ASGTU
   681  		p.From.Type = obj.TYPE_REG
   682  		p.From.Reg = REGSP
   683  		p.Reg = REG_R1
   684  		p.To.Type = obj.TYPE_REG
   685  		p.To.Reg = REG_R1
   686  	} else if framesize <= objabi.StackBig {
   687  		// large stack: SP-framesize < stackguard-StackSmall
   688  		//	ADD	$-(framesize-StackSmall), SP, R2
   689  		//	SGTU	R2, stackguard, R1
   690  		p = obj.Appendp(p, c.newprog)
   691  
   692  		p.As = add
   693  		p.From.Type = obj.TYPE_CONST
   694  		p.From.Offset = -(int64(framesize) - objabi.StackSmall)
   695  		p.Reg = REGSP
   696  		p.To.Type = obj.TYPE_REG
   697  		p.To.Reg = REG_R2
   698  
   699  		p = obj.Appendp(p, c.newprog)
   700  		p.As = ASGTU
   701  		p.From.Type = obj.TYPE_REG
   702  		p.From.Reg = REG_R2
   703  		p.Reg = REG_R1
   704  		p.To.Type = obj.TYPE_REG
   705  		p.To.Reg = REG_R1
   706  	} else {
   707  		// Such a large stack we need to protect against wraparound.
   708  		// If SP is close to zero:
   709  		//	SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
   710  		// The +StackGuard on both sides is required to keep the left side positive:
   711  		// SP is allowed to be slightly below stackguard. See stack.h.
   712  		//
   713  		// Preemption sets stackguard to StackPreempt, a very large value.
   714  		// That breaks the math above, so we have to check for that explicitly.
   715  		//	// stackguard is R1
   716  		//	MOV	$StackPreempt, R2
   717  		//	BEQ	R1, R2, label-of-call-to-morestack
   718  		//	ADD	$StackGuard, SP, R2
   719  		//	SUB	R1, R2
   720  		//	MOV	$(framesize+(StackGuard-StackSmall)), R1
   721  		//	SGTU	R2, R1, R1
   722  		p = obj.Appendp(p, c.newprog)
   723  
   724  		p.As = mov
   725  		p.From.Type = obj.TYPE_CONST
   726  		p.From.Offset = objabi.StackPreempt
   727  		p.To.Type = obj.TYPE_REG
   728  		p.To.Reg = REG_R2
   729  
   730  		p = obj.Appendp(p, c.newprog)
   731  		q = p
   732  		p.As = ABEQ
   733  		p.From.Type = obj.TYPE_REG
   734  		p.From.Reg = REG_R1
   735  		p.Reg = REG_R2
   736  		p.To.Type = obj.TYPE_BRANCH
   737  		p.Mark |= BRANCH
   738  
   739  		p = obj.Appendp(p, c.newprog)
   740  		p.As = add
   741  		p.From.Type = obj.TYPE_CONST
   742  		p.From.Offset = int64(objabi.StackGuard)
   743  		p.Reg = REGSP
   744  		p.To.Type = obj.TYPE_REG
   745  		p.To.Reg = REG_R2
   746  
   747  		p = obj.Appendp(p, c.newprog)
   748  		p.As = sub
   749  		p.From.Type = obj.TYPE_REG
   750  		p.From.Reg = REG_R1
   751  		p.To.Type = obj.TYPE_REG
   752  		p.To.Reg = REG_R2
   753  
   754  		p = obj.Appendp(p, c.newprog)
   755  		p.As = mov
   756  		p.From.Type = obj.TYPE_CONST
   757  		p.From.Offset = int64(framesize) + int64(objabi.StackGuard) - objabi.StackSmall
   758  		p.To.Type = obj.TYPE_REG
   759  		p.To.Reg = REG_R1
   760  
   761  		p = obj.Appendp(p, c.newprog)
   762  		p.As = ASGTU
   763  		p.From.Type = obj.TYPE_REG
   764  		p.From.Reg = REG_R2
   765  		p.Reg = REG_R1
   766  		p.To.Type = obj.TYPE_REG
   767  		p.To.Reg = REG_R1
   768  	}
   769  
   770  	// q1: BNE	R1, done
   771  	p = obj.Appendp(p, c.newprog)
   772  	q1 := p
   773  
   774  	p.As = ABNE
   775  	p.From.Type = obj.TYPE_REG
   776  	p.From.Reg = REG_R1
   777  	p.To.Type = obj.TYPE_BRANCH
   778  	p.Mark |= BRANCH
   779  
   780  	// MOV	LINK, R3
   781  	p = obj.Appendp(p, c.newprog)
   782  
   783  	p.As = mov
   784  	p.From.Type = obj.TYPE_REG
   785  	p.From.Reg = REGLINK
   786  	p.To.Type = obj.TYPE_REG
   787  	p.To.Reg = REG_R3
   788  	if q != nil {
   789  		q.Pcond = p
   790  		p.Mark |= LABEL
   791  	}
   792  
   793  	p = c.ctxt.EmitEntryLiveness(c.cursym, p, c.newprog)
   794  
   795  	// JAL	runtime.morestack(SB)
   796  	p = obj.Appendp(p, c.newprog)
   797  
   798  	p.As = AJAL
   799  	p.To.Type = obj.TYPE_BRANCH
   800  	if c.cursym.CFunc() {
   801  		p.To.Sym = c.ctxt.Lookup("runtime.morestackc")
   802  	} else if !c.cursym.Func.Text.From.Sym.NeedCtxt() {
   803  		p.To.Sym = c.ctxt.Lookup("runtime.morestack_noctxt")
   804  	} else {
   805  		p.To.Sym = c.ctxt.Lookup("runtime.morestack")
   806  	}
   807  	p.Mark |= BRANCH
   808  
   809  	// JMP	start
   810  	p = obj.Appendp(p, c.newprog)
   811  
   812  	p.As = AJMP
   813  	p.To.Type = obj.TYPE_BRANCH
   814  	p.Pcond = c.cursym.Func.Text.Link
   815  	p.Mark |= BRANCH
   816  
   817  	// placeholder for q1's jump target
   818  	p = obj.Appendp(p, c.newprog)
   819  
   820  	p.As = obj.ANOP // zero-width place holder
   821  	q1.Pcond = p
   822  
   823  	return p
   824  }
   825  
   826  func (c *ctxt0) addnop(p *obj.Prog) {
   827  	q := c.newprog()
   828  	q.As = ANOOP
   829  	q.Pos = p.Pos
   830  	q.Link = p.Link
   831  	p.Link = q
   832  }
   833  
   834  const (
   835  	E_HILO  = 1 << 0
   836  	E_FCR   = 1 << 1
   837  	E_MCR   = 1 << 2
   838  	E_MEM   = 1 << 3
   839  	E_MEMSP = 1 << 4 /* uses offset and size */
   840  	E_MEMSB = 1 << 5 /* uses offset and size */
   841  	ANYMEM  = E_MEM | E_MEMSP | E_MEMSB
   842  	//DELAY = LOAD|BRANCH|FCMP
   843  	DELAY = BRANCH /* only schedule branch */
   844  )
   845  
   846  type Dep struct {
   847  	ireg uint32
   848  	freg uint32
   849  	cc   uint32
   850  }
   851  
   852  type Sch struct {
   853  	p       obj.Prog
   854  	set     Dep
   855  	used    Dep
   856  	soffset int32
   857  	size    uint8
   858  	nop     uint8
   859  	comp    bool
   860  }
   861  
   862  func (c *ctxt0) sched(p0, pe *obj.Prog) {
   863  	var sch [NSCHED]Sch
   864  
   865  	/*
   866  	 * build side structure
   867  	 */
   868  	s := sch[:]
   869  	for p := p0; ; p = p.Link {
   870  		s[0].p = *p
   871  		c.markregused(&s[0])
   872  		if p == pe {
   873  			break
   874  		}
   875  		s = s[1:]
   876  	}
   877  	se := s
   878  
   879  	for i := cap(sch) - cap(se); i >= 0; i-- {
   880  		s = sch[i:]
   881  		if s[0].p.Mark&DELAY == 0 {
   882  			continue
   883  		}
   884  		if -cap(s) < -cap(se) {
   885  			if !conflict(&s[0], &s[1]) {
   886  				continue
   887  			}
   888  		}
   889  
   890  		var t []Sch
   891  		var j int
   892  		for j = cap(sch) - cap(s) - 1; j >= 0; j-- {
   893  			t = sch[j:]
   894  			if t[0].comp {
   895  				if s[0].p.Mark&BRANCH != 0 {
   896  					continue
   897  				}
   898  			}
   899  			if t[0].p.Mark&DELAY != 0 {
   900  				if -cap(s) >= -cap(se) || conflict(&t[0], &s[1]) {
   901  					continue
   902  				}
   903  			}
   904  			for u := t[1:]; -cap(u) <= -cap(s); u = u[1:] {
   905  				if c.depend(&u[0], &t[0]) {
   906  					continue
   907  				}
   908  			}
   909  			goto out2
   910  		}
   911  
   912  		if s[0].p.Mark&BRANCH != 0 {
   913  			s[0].nop = 1
   914  		}
   915  		continue
   916  
   917  	out2:
   918  		// t[0] is the instruction being moved to fill the delay
   919  		stmp := t[0]
   920  		copy(t[:i-j], t[1:i-j+1])
   921  		s[0] = stmp
   922  
   923  		if t[i-j-1].p.Mark&BRANCH != 0 {
   924  			// t[i-j] is being put into a branch delay slot
   925  			// combine its Spadj with the branch instruction
   926  			t[i-j-1].p.Spadj += t[i-j].p.Spadj
   927  			t[i-j].p.Spadj = 0
   928  		}
   929  
   930  		i--
   931  	}
   932  
   933  	/*
   934  	 * put it all back
   935  	 */
   936  	var p *obj.Prog
   937  	var q *obj.Prog
   938  	for s, p = sch[:], p0; -cap(s) <= -cap(se); s, p = s[1:], q {
   939  		q = p.Link
   940  		if q != s[0].p.Link {
   941  			*p = s[0].p
   942  			p.Link = q
   943  		}
   944  		for s[0].nop != 0 {
   945  			s[0].nop--
   946  			c.addnop(p)
   947  		}
   948  	}
   949  }
   950  
   951  func (c *ctxt0) markregused(s *Sch) {
   952  	p := &s.p
   953  	s.comp = c.compound(p)
   954  	s.nop = 0
   955  	if s.comp {
   956  		s.set.ireg |= 1 << (REGTMP - REG_R0)
   957  		s.used.ireg |= 1 << (REGTMP - REG_R0)
   958  	}
   959  
   960  	ar := 0  /* dest is really reference */
   961  	ad := 0  /* source/dest is really address */
   962  	ld := 0  /* opcode is load instruction */
   963  	sz := 20 /* size of load/store for overlap computation */
   964  
   965  	/*
   966  	 * flags based on opcode
   967  	 */
   968  	switch p.As {
   969  	case obj.ATEXT:
   970  		c.autosize = int32(p.To.Offset + 8)
   971  		ad = 1
   972  
   973  	case AJAL:
   974  		r := p.Reg
   975  		if r == 0 {
   976  			r = REGLINK
   977  		}
   978  		s.set.ireg |= 1 << uint(r-REG_R0)
   979  		ar = 1
   980  		ad = 1
   981  
   982  	case ABGEZAL,
   983  		ABLTZAL:
   984  		s.set.ireg |= 1 << (REGLINK - REG_R0)
   985  		fallthrough
   986  	case ABEQ,
   987  		ABGEZ,
   988  		ABGTZ,
   989  		ABLEZ,
   990  		ABLTZ,
   991  		ABNE:
   992  		ar = 1
   993  		ad = 1
   994  
   995  	case ABFPT,
   996  		ABFPF:
   997  		ad = 1
   998  		s.used.cc |= E_FCR
   999  
  1000  	case ACMPEQD,
  1001  		ACMPEQF,
  1002  		ACMPGED,
  1003  		ACMPGEF,
  1004  		ACMPGTD,
  1005  		ACMPGTF:
  1006  		ar = 1
  1007  		s.set.cc |= E_FCR
  1008  		p.Mark |= FCMP
  1009  
  1010  	case AJMP:
  1011  		ar = 1
  1012  		ad = 1
  1013  
  1014  	case AMOVB,
  1015  		AMOVBU:
  1016  		sz = 1
  1017  		ld = 1
  1018  
  1019  	case AMOVH,
  1020  		AMOVHU:
  1021  		sz = 2
  1022  		ld = 1
  1023  
  1024  	case AMOVF,
  1025  		AMOVW,
  1026  		AMOVWL,
  1027  		AMOVWR:
  1028  		sz = 4
  1029  		ld = 1
  1030  
  1031  	case AMOVD,
  1032  		AMOVV,
  1033  		AMOVVL,
  1034  		AMOVVR:
  1035  		sz = 8
  1036  		ld = 1
  1037  
  1038  	case ADIV,
  1039  		ADIVU,
  1040  		AMUL,
  1041  		AMULU,
  1042  		AREM,
  1043  		AREMU,
  1044  		ADIVV,
  1045  		ADIVVU,
  1046  		AMULV,
  1047  		AMULVU,
  1048  		AREMV,
  1049  		AREMVU:
  1050  		s.set.cc = E_HILO
  1051  		fallthrough
  1052  	case AADD,
  1053  		AADDU,
  1054  		AADDV,
  1055  		AADDVU,
  1056  		AAND,
  1057  		ANOR,
  1058  		AOR,
  1059  		ASGT,
  1060  		ASGTU,
  1061  		ASLL,
  1062  		ASRA,
  1063  		ASRL,
  1064  		ASLLV,
  1065  		ASRAV,
  1066  		ASRLV,
  1067  		ASUB,
  1068  		ASUBU,
  1069  		ASUBV,
  1070  		ASUBVU,
  1071  		AXOR,
  1072  
  1073  		AADDD,
  1074  		AADDF,
  1075  		AADDW,
  1076  		ASUBD,
  1077  		ASUBF,
  1078  		ASUBW,
  1079  		AMULF,
  1080  		AMULD,
  1081  		AMULW,
  1082  		ADIVF,
  1083  		ADIVD,
  1084  		ADIVW:
  1085  		if p.Reg == 0 {
  1086  			if p.To.Type == obj.TYPE_REG {
  1087  				p.Reg = p.To.Reg
  1088  			}
  1089  			//if(p->reg == NREG)
  1090  			//	print("botch %P\n", p);
  1091  		}
  1092  	}
  1093  
  1094  	/*
  1095  	 * flags based on 'to' field
  1096  	 */
  1097  	cls := int(p.To.Class)
  1098  	if cls == 0 {
  1099  		cls = c.aclass(&p.To) + 1
  1100  		p.To.Class = int8(cls)
  1101  	}
  1102  	cls--
  1103  	switch cls {
  1104  	default:
  1105  		fmt.Printf("unknown class %d %v\n", cls, p)
  1106  
  1107  	case C_ZCON,
  1108  		C_SCON,
  1109  		C_ADD0CON,
  1110  		C_AND0CON,
  1111  		C_ADDCON,
  1112  		C_ANDCON,
  1113  		C_UCON,
  1114  		C_LCON,
  1115  		C_NONE,
  1116  		C_SBRA,
  1117  		C_LBRA,
  1118  		C_ADDR,
  1119  		C_TEXTSIZE:
  1120  		break
  1121  
  1122  	case C_HI,
  1123  		C_LO:
  1124  		s.set.cc |= E_HILO
  1125  
  1126  	case C_FCREG:
  1127  		s.set.cc |= E_FCR
  1128  
  1129  	case C_MREG:
  1130  		s.set.cc |= E_MCR
  1131  
  1132  	case C_ZOREG,
  1133  		C_SOREG,
  1134  		C_LOREG:
  1135  		cls = int(p.To.Reg)
  1136  		s.used.ireg |= 1 << uint(cls-REG_R0)
  1137  		if ad != 0 {
  1138  			break
  1139  		}
  1140  		s.size = uint8(sz)
  1141  		s.soffset = c.regoff(&p.To)
  1142  
  1143  		m := uint32(ANYMEM)
  1144  		if cls == REGSB {
  1145  			m = E_MEMSB
  1146  		}
  1147  		if cls == REGSP {
  1148  			m = E_MEMSP
  1149  		}
  1150  
  1151  		if ar != 0 {
  1152  			s.used.cc |= m
  1153  		} else {
  1154  			s.set.cc |= m
  1155  		}
  1156  
  1157  	case C_SACON,
  1158  		C_LACON:
  1159  		s.used.ireg |= 1 << (REGSP - REG_R0)
  1160  
  1161  	case C_SECON,
  1162  		C_LECON:
  1163  		s.used.ireg |= 1 << (REGSB - REG_R0)
  1164  
  1165  	case C_REG:
  1166  		if ar != 0 {
  1167  			s.used.ireg |= 1 << uint(p.To.Reg-REG_R0)
  1168  		} else {
  1169  			s.set.ireg |= 1 << uint(p.To.Reg-REG_R0)
  1170  		}
  1171  
  1172  	case C_FREG:
  1173  		if ar != 0 {
  1174  			s.used.freg |= 1 << uint(p.To.Reg-REG_F0)
  1175  		} else {
  1176  			s.set.freg |= 1 << uint(p.To.Reg-REG_F0)
  1177  		}
  1178  		if ld != 0 && p.From.Type == obj.TYPE_REG {
  1179  			p.Mark |= LOAD
  1180  		}
  1181  
  1182  	case C_SAUTO,
  1183  		C_LAUTO:
  1184  		s.used.ireg |= 1 << (REGSP - REG_R0)
  1185  		if ad != 0 {
  1186  			break
  1187  		}
  1188  		s.size = uint8(sz)
  1189  		s.soffset = c.regoff(&p.To)
  1190  
  1191  		if ar != 0 {
  1192  			s.used.cc |= E_MEMSP
  1193  		} else {
  1194  			s.set.cc |= E_MEMSP
  1195  		}
  1196  
  1197  	case C_SEXT,
  1198  		C_LEXT:
  1199  		s.used.ireg |= 1 << (REGSB - REG_R0)
  1200  		if ad != 0 {
  1201  			break
  1202  		}
  1203  		s.size = uint8(sz)
  1204  		s.soffset = c.regoff(&p.To)
  1205  
  1206  		if ar != 0 {
  1207  			s.used.cc |= E_MEMSB
  1208  		} else {
  1209  			s.set.cc |= E_MEMSB
  1210  		}
  1211  	}
  1212  
  1213  	/*
  1214  	 * flags based on 'from' field
  1215  	 */
  1216  	cls = int(p.From.Class)
  1217  	if cls == 0 {
  1218  		cls = c.aclass(&p.From) + 1
  1219  		p.From.Class = int8(cls)
  1220  	}
  1221  	cls--
  1222  	switch cls {
  1223  	default:
  1224  		fmt.Printf("unknown class %d %v\n", cls, p)
  1225  
  1226  	case C_ZCON,
  1227  		C_SCON,
  1228  		C_ADD0CON,
  1229  		C_AND0CON,
  1230  		C_ADDCON,
  1231  		C_ANDCON,
  1232  		C_UCON,
  1233  		C_LCON,
  1234  		C_NONE,
  1235  		C_SBRA,
  1236  		C_LBRA,
  1237  		C_ADDR,
  1238  		C_TEXTSIZE:
  1239  		break
  1240  
  1241  	case C_HI,
  1242  		C_LO:
  1243  		s.used.cc |= E_HILO
  1244  
  1245  	case C_FCREG:
  1246  		s.used.cc |= E_FCR
  1247  
  1248  	case C_MREG:
  1249  		s.used.cc |= E_MCR
  1250  
  1251  	case C_ZOREG,
  1252  		C_SOREG,
  1253  		C_LOREG:
  1254  		cls = int(p.From.Reg)
  1255  		s.used.ireg |= 1 << uint(cls-REG_R0)
  1256  		if ld != 0 {
  1257  			p.Mark |= LOAD
  1258  		}
  1259  		s.size = uint8(sz)
  1260  		s.soffset = c.regoff(&p.From)
  1261  
  1262  		m := uint32(ANYMEM)
  1263  		if cls == REGSB {
  1264  			m = E_MEMSB
  1265  		}
  1266  		if cls == REGSP {
  1267  			m = E_MEMSP
  1268  		}
  1269  
  1270  		s.used.cc |= m
  1271  
  1272  	case C_SACON,
  1273  		C_LACON:
  1274  		cls = int(p.From.Reg)
  1275  		if cls == 0 {
  1276  			cls = REGSP
  1277  		}
  1278  		s.used.ireg |= 1 << uint(cls-REG_R0)
  1279  
  1280  	case C_SECON,
  1281  		C_LECON:
  1282  		s.used.ireg |= 1 << (REGSB - REG_R0)
  1283  
  1284  	case C_REG:
  1285  		s.used.ireg |= 1 << uint(p.From.Reg-REG_R0)
  1286  
  1287  	case C_FREG:
  1288  		s.used.freg |= 1 << uint(p.From.Reg-REG_F0)
  1289  		if ld != 0 && p.To.Type == obj.TYPE_REG {
  1290  			p.Mark |= LOAD
  1291  		}
  1292  
  1293  	case C_SAUTO,
  1294  		C_LAUTO:
  1295  		s.used.ireg |= 1 << (REGSP - REG_R0)
  1296  		if ld != 0 {
  1297  			p.Mark |= LOAD
  1298  		}
  1299  		if ad != 0 {
  1300  			break
  1301  		}
  1302  		s.size = uint8(sz)
  1303  		s.soffset = c.regoff(&p.From)
  1304  
  1305  		s.used.cc |= E_MEMSP
  1306  
  1307  	case C_SEXT:
  1308  	case C_LEXT:
  1309  		s.used.ireg |= 1 << (REGSB - REG_R0)
  1310  		if ld != 0 {
  1311  			p.Mark |= LOAD
  1312  		}
  1313  		if ad != 0 {
  1314  			break
  1315  		}
  1316  		s.size = uint8(sz)
  1317  		s.soffset = c.regoff(&p.From)
  1318  
  1319  		s.used.cc |= E_MEMSB
  1320  	}
  1321  
  1322  	cls = int(p.Reg)
  1323  	if cls != 0 {
  1324  		if REG_F0 <= cls && cls <= REG_F31 {
  1325  			s.used.freg |= 1 << uint(cls-REG_F0)
  1326  		} else {
  1327  			s.used.ireg |= 1 << uint(cls-REG_R0)
  1328  		}
  1329  	}
  1330  	s.set.ireg &^= (1 << (REGZERO - REG_R0)) /* R0 can't be set */
  1331  }
  1332  
  1333  /*
  1334   * test to see if two instructions can be
  1335   * interchanged without changing semantics
  1336   */
  1337  func (c *ctxt0) depend(sa, sb *Sch) bool {
  1338  	if sa.set.ireg&(sb.set.ireg|sb.used.ireg) != 0 {
  1339  		return true
  1340  	}
  1341  	if sb.set.ireg&sa.used.ireg != 0 {
  1342  		return true
  1343  	}
  1344  
  1345  	if sa.set.freg&(sb.set.freg|sb.used.freg) != 0 {
  1346  		return true
  1347  	}
  1348  	if sb.set.freg&sa.used.freg != 0 {
  1349  		return true
  1350  	}
  1351  
  1352  	/*
  1353  	 * special case.
  1354  	 * loads from same address cannot pass.
  1355  	 * this is for hardware fifo's and the like
  1356  	 */
  1357  	if sa.used.cc&sb.used.cc&E_MEM != 0 {
  1358  		if sa.p.Reg == sb.p.Reg {
  1359  			if c.regoff(&sa.p.From) == c.regoff(&sb.p.From) {
  1360  				return true
  1361  			}
  1362  		}
  1363  	}
  1364  
  1365  	x := (sa.set.cc & (sb.set.cc | sb.used.cc)) | (sb.set.cc & sa.used.cc)
  1366  	if x != 0 {
  1367  		/*
  1368  		 * allow SB and SP to pass each other.
  1369  		 * allow SB to pass SB iff doffsets are ok
  1370  		 * anything else conflicts
  1371  		 */
  1372  		if x != E_MEMSP && x != E_MEMSB {
  1373  			return true
  1374  		}
  1375  		x = sa.set.cc | sb.set.cc | sa.used.cc | sb.used.cc
  1376  		if x&E_MEM != 0 {
  1377  			return true
  1378  		}
  1379  		if offoverlap(sa, sb) {
  1380  			return true
  1381  		}
  1382  	}
  1383  
  1384  	return false
  1385  }
  1386  
  1387  func offoverlap(sa, sb *Sch) bool {
  1388  	if sa.soffset < sb.soffset {
  1389  		if sa.soffset+int32(sa.size) > sb.soffset {
  1390  			return true
  1391  		}
  1392  		return false
  1393  	}
  1394  	if sb.soffset+int32(sb.size) > sa.soffset {
  1395  		return true
  1396  	}
  1397  	return false
  1398  }
  1399  
  1400  /*
  1401   * test 2 adjacent instructions
  1402   * and find out if inserted instructions
  1403   * are desired to prevent stalls.
  1404   */
  1405  func conflict(sa, sb *Sch) bool {
  1406  	if sa.set.ireg&sb.used.ireg != 0 {
  1407  		return true
  1408  	}
  1409  	if sa.set.freg&sb.used.freg != 0 {
  1410  		return true
  1411  	}
  1412  	if sa.set.cc&sb.used.cc != 0 {
  1413  		return true
  1414  	}
  1415  	return false
  1416  }
  1417  
  1418  func (c *ctxt0) compound(p *obj.Prog) bool {
  1419  	o := c.oplook(p)
  1420  	if o.size != 4 {
  1421  		return true
  1422  	}
  1423  	if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSB {
  1424  		return true
  1425  	}
  1426  	return false
  1427  }
  1428  
  1429  var Linkmips64 = obj.LinkArch{
  1430  	Arch:           sys.ArchMIPS64,
  1431  	Init:           buildop,
  1432  	Preprocess:     preprocess,
  1433  	Assemble:       span0,
  1434  	Progedit:       progedit,
  1435  	DWARFRegisters: MIPSDWARFRegisters,
  1436  }
  1437  
  1438  var Linkmips64le = obj.LinkArch{
  1439  	Arch:           sys.ArchMIPS64LE,
  1440  	Init:           buildop,
  1441  	Preprocess:     preprocess,
  1442  	Assemble:       span0,
  1443  	Progedit:       progedit,
  1444  	DWARFRegisters: MIPSDWARFRegisters,
  1445  }
  1446  
  1447  var Linkmips = obj.LinkArch{
  1448  	Arch:           sys.ArchMIPS,
  1449  	Init:           buildop,
  1450  	Preprocess:     preprocess,
  1451  	Assemble:       span0,
  1452  	Progedit:       progedit,
  1453  	DWARFRegisters: MIPSDWARFRegisters,
  1454  }
  1455  
  1456  var Linkmipsle = obj.LinkArch{
  1457  	Arch:           sys.ArchMIPSLE,
  1458  	Init:           buildop,
  1459  	Preprocess:     preprocess,
  1460  	Assemble:       span0,
  1461  	Progedit:       progedit,
  1462  	DWARFRegisters: MIPSDWARFRegisters,
  1463  }