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