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