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