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